是否可以为类型参数指定一个通用约束,以便从另一个类型转换?
假设我用以下内容编写了一个库:
public class Bar { /* ... */ } public class SomeWeirdClass where T : ??? { public T BarMaker(Bar b) { // ... play with b T t = (T)b return (T) b; } }
后来,我希望用户通过定义他们自己的类型来使用我的库,这些类型可以转换为Bar并使用SomeWeirdClass“factory”。
public class Foo { public static explicit operator Foo(Bar f) { return new Bar(); } } public class Demo { public static void demo() { Bar b = new Bar(); SomeWeirdClass weird = new SomeWeirdClass(); Foo f = weird.BarMaker(b); } }
这将编译,如果我设置where T : Foo
但问题是我在库的编译时不知道Foo,我实际上想要更像where T : some class that can be instantiated, given a Bar
这可能吗? 从我有限的知识来看,它似乎并不是,但.NET框架及其用户的独创性总让我惊喜……
这可能与静态接口方法的概念有关 – 至少,我可以看到能够指定工厂方法的存在以创建对象的价值(类似于你已经可以where T : new()
执行的方式) where T : new()
)
编辑: 解决方案 – 感谢Nick和bzIm – 对于其他读者,我将提供一个完整的解决方案,因为我理解它:edit2:此解决方案需要Foo公开一个公共默认构造函数。 对于一个甚至更愚蠢的更好的解决方案,不需要这个看到这篇文章的最底层。
public class Bar {} public class SomeWeirdClass where T : IConvertibleFromBar, new() { public T BarMaker(Bar b) { T t = new T(); t.Convert(b); return t; } } public interface IConvertibleFromBar { T Convert(Bar b); } public class Foo : IConvertibleFromBar { public static explicit operator Foo(Bar f) { return null; } public Foo Convert(Bar b) { return (Foo) b; } } public class Demo { public static void demo() { Bar b = new Bar(); SomeWeirdClass weird = new SomeWeirdClass(); Foo f = weird.BarMaker(b); } }
edit2: 解决方案2 :创建一个类型转换器工厂使用:
#region library defined code public class Bar {} public class SomeWeirdClass where TFactory : IConvertorFactory, new() { private static TFactory convertor = new TFactory(); public T BarMaker(Bar b) { return convertor.Convert(b); } } public interface IConvertorFactory { TTo Convert(TFrom from); } #endregion #region user defined code public class BarToFooConvertor : IConvertorFactory { public Foo Convert(Bar from) { return (Foo) from; } } public class Foo { public Foo(int a) {} public static explicit operator Foo(Bar f) { return null; } public Foo Convert(Bar b) { return (Foo) b; } } #endregion public class Demo { public static void demo() { Bar b = new Bar(); SomeWeirdClass weird = new SomeWeirdClass(); Foo f = weird.BarMaker(b); } }
我认为在语言中内置这种语法并不一定有一种语法上很酷的方法。 您的问题的一个可能的解决方案可能是定义可转换的接口:
public interface IConvertible where T : new() // Probably will need this { T Convert(); }
然后你的class级可能是:
public class Foo : IConvertible { }
我认为这会让你接近你想要的地方……你的问题中的所有Foo和Bar都有时难以确定你的意图。 希望这可以帮助。
编辑:添加约束…您可能必须能够在您的可转换类中创建一个新实例。
编辑2: Made Fooinheritance自ICovertible
听起来你找到了解决更大问题的方法。 回答您的具体问题:不,C#和CLR都不支持“向后”generics类型参数约束。 那是,
class C where Foo : T
“T必须是Foo或Foo转换为的类型”不受支持。
有些语言有这种约束; IIRC Scala就是这样一种语言。 我怀疑这个function对于逆变接口的某些用途会很方便。
您可以通过用作类型约束的接口绕道而行。
例如,使用where T : IComparable
将类型约束为可以与另一个事物进行比较的事物,这必须通过实现IComparable
来表达这种能力。 如果你有一个接口ICastableFrom
,你可以通过强制他们实现ICastableFrom
来实现你想要的。
而不是经历定义接口和修改类来实现该接口的麻烦,为什么不这样做呢?
public class SomeWeirdClass { // aside: why is this method called 'BarMaker' if it returns a T? public T BarMaker(Bar b, Func converter) { // ... play with b return converter(b); } }
然后,如果您正在处理可以直接转换为Bar
的类型T
的对象,则可以按如下方式调用此方法:
var someWeirdObject = new SomeWeirdClass(); var someBar = new Bar(); var someFoo = someWeirdObjcet.BarMaker(someBar, bar => bar as Foo);
顺便说一下(因为Func
委托出现在.NET 3.5中),你也可以使用Converter
(这是完全相同的) converter
参数。