簡體   English   中英

如何在沒有特定類型的情況下實現泛型接口?

[英]How to implement a generics interface without specific types?

給定這樣的接口:

public interface Transformer<TSource, TResult> {
    TResult Transform(TSource original);
}

我想為不需要轉換的情況提供一個簡單的“ no-op”接口實現,它只返回原始對象本身。 在這種情況下, TSourceTResult將相同。

作為一個仍在學習C#的Java老兄,我的第一個想法就是:

public class NoopTransformer : Transformer<object, object> {
    public override object Transform(object original) {
        return original;
    }
}

可以編譯,但是我幾乎不能在只需要Transformer地方使用它。 如果嘗試,會出現編譯錯誤,指出NoopTransformer不能用作Transformer<TSource, TResult>

我渴望的是Java的通配符或願意將Object視為任何對象的可接受類型參數。 C#中真的沒有等效功能嗎?


然后我認為這會起作用:

public class NoopTransformer<TSource> : Transformer<TSource, TSource> {
    public override TSource Transform(TSource original) {
        return entity;
    }
}

在任何需要Transformer<TSource, TResult>地方都無法使用,像這樣:

public class Controller<TEntity, TResult> {
    private Transformer<TEntity, TResult> transformer;

    public Controller() : this(new NoopTransformer<TEntity>()) // error on this line
        { }

    public Controller(Transformer<TEntity, TResult> transformer) {
        this.transformer = transformer;
    }
}

無法編譯,說:

  • 無法從NoopTransformer<TEntity> Transformer<TSource, TResult>Transformer<TSource, TResult>
  • 類型TEntity與預期的類型TResult不匹配。
  • 參數類型NoopTransformer<TEntity>不可分配給參數類型Transformer<TEntity, TResult>

(即使似乎我的隱含滿足了該約定TSourceTResult相同的事實也沒有關系)。


最后,我抓住稻草,嘗試了一下:

public class NoopTransformer<TSource, TResult> : Transformer<TSource, TResult>
    where TResult : TSource
{
    public override TResult Transform(TSource original) {
        return (TResult)entity;
    }
}

但是當然這是行不通的,因為我想在哪里使用它,沒有約束說TResult必須擴展TSource


上下文是我有另一個具有相同TSourceTResult類型參數的類,並且可以在其構造函數中接受Transformer 但是,在該類中,變壓器是可選的。 在客戶端未指定更特定的Transformer情況下,我想使用NoopTransformer

在Java中,這很簡單,實際上有兩種不同的解決方法(使用類型通配符, extends等)。 我意識到Java更為寬容(由於使用了類型擦除),因此固有地更加靈活,因為在運行時沒有類型參數。 但是,似乎C#編譯器過於嚴格(也許有些愚蠢)。

如何實現為接口提供有用的默認值的目標?

編譯器正確拒絕

public Controller() : this(new NoopTransformer<TEntity>())

由於Transformer<TEntity, TEntity>Transformer<TEntity, TResult>不兼容,因為不能保證TEntityTResult是相同的,例如

var c = new Controller<string, int>();

您可以通過創建工廠方法來構造TEntityTResult相同的Controller實例來維護靜態安全性:

public static class Controller
{
    public static Controller<TEntity, TEntity> Create<TEntity>()
    {
        return new Controller<TEntity, TEntity>(new IdentityTransformer<TEntity>());
    }
}

如果由於某種原因您不能采用這種方法,則必須在運行時依賴轉換:

public class CastingTransformer<TSource, TResult> : Transformer<TSource, TResult>
{
    public TResult Transform(TSource original)
    {
        return (TResult)(object)original;
    }
}

public Controller() : this(new CastingTransformer<TEntity, TResult>()) { }

這就是您的想法嗎?

public interface Transformer<TSource, TResult>
{
    TResult Transform(TSource original);
}

public class NoopTransformer<TSource, TResult> : Transformer<TSource, TResult> where TResult : class
{
    public TResult Transform(TSource original)
    {
        return original as TResult;
    }
}

public class Controller<TEntity, TResult> where TResult : class
{
    private Transformer<TEntity, TResult> transformer;

    public Controller() : this(new NoopTransformer<TEntity, TResult>())
    { }

    public Controller(Transformer<TEntity, TResult> transformer)
    {
        this.transformer = transformer;
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM