给定这样的界面:
public interface Transformer<TSource, TResult> {
TResult Transform(TSource original);
}
我希望为不需要转换的情况提供该接口的简单“无操作”实现,这只会返回原始对象本身。在这种情况下,TSource
和TResult
将是相同的。
作为一个仍在学习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>
NoopTransformer<TEntity>
Transformer<TEntity, TResult>
(尽管我的impl似乎很明显满足了这个合同 - TSource
和TResult
相同的事实应该无关紧要。)
最后,抓住稻草,我试了一下:
public class NoopTransformer<TSource, TResult> : Transformer<TSource, TResult>
where TResult : TSource
{
public override TResult Transform(TSource original) {
return (TResult)entity;
}
}
但当然这不起作用,因为在我想要使用它时,没有约束表明TResult
必须扩展TSource
。
上下文是我有另一个具有相同TSource
和TResult
类型参数的类,并且可以在其构造函数中接受Transformer
。但是变压器在那个级别是可选的;我希望在客户端未指定更具体的NoopTransformer
的情况下使用Transformer
。
在Java中,这是微不足道的,事实上有几种不同的解决方法(使用类型通配符,extends
等)。我意识到Java更宽松(由于它使用了类型擦除),因此在运行时没有类型参数,因此本质上更灵活。但似乎C#编译器不必要地严格(可能有点愚蠢)。
如何实现为界面提供有用默认值的目标?
答案 0 :(得分:2)
编译器拒绝
是正确的public Controller() : this(new NoopTransformer<TEntity>())
由于Transformer<TEntity, TEntity>
与Transformer<TEntity, TResult>
不兼容,因为TEntity
和TResult
不能保证相同,例如
var c = new Controller<string, int>();
您可以通过创建工厂方法来构建Controller
实例来保持静态安全,其中TEntity
和TResult
相同:
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