如何实现没有特定类型的泛型接口?

时间:2017-04-18 17:11:58

标签: c# generics

给定这样的界面:

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

我希望为不需要转换的情况提供该接口的简单“无操作”实现,这只会返回原始对象本身。在这种情况下,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>
  • 类型TEntity与预期类型TResult不匹配。
  • 参数类型NoopTransformer<TEntity>
  • 无法分配参数类型Transformer<TEntity, TResult>

(尽管我的impl似乎很明显满足了这个合同 - 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。但是变压器在那个级别是可选的;我希望在客户端未指定更具体的NoopTransformer的情况下使用Transformer

在Java中,这是微不足道的,事实上有几种不同的解决方法(使用类型通配符,extends等)。我意识到Java更宽松(由于它使用了类型擦除),因此在运行时没有类型参数,因此本质上更灵活。但似乎C#编译器不必要地严格(可能有点愚蠢)。

如何实现为界面提供有用默认值的目标?

2 个答案:

答案 0 :(得分:2)

编译器拒绝

是正确的
public Controller() : this(new NoopTransformer<TEntity>())

由于Transformer<TEntity, TEntity>Transformer<TEntity, TResult>不兼容,因为TEntityTResult不能保证相同,例如

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

您可以通过创建工厂方法来构建Controller实例来保持静态安全,其中TEntityTResult相同:

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>()) { }

答案 1 :(得分:0)

这是你的想法吗?

GetHashCode