Is it possible to specify a generic constraint for a type parameter to be convertible FROM another type?
假设我编写了一个包含以下内容的库:
1 2 3 4 5 6 7 8 9 10 11 12 | public class Bar { /* ... */ } public class SomeWeirdClass<T> where T : ??? { public T BarMaker(Bar b) { // ... play with b T t = (T)b return (T) b; } } |
稍后,我希望用户使用我的库来定义他们自己的类型,这些类型可以转换为条,并使用一些奇怪的类"工厂"。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
如果我设置
这有可能吗?从我有限的知识来看,它似乎不是,但.NET框架及其用户的独创性总是让我吃惊…
这可能与静态接口方法的想法有关,也可能与之无关——至少,我可以看到能够指定工厂方法来创建对象的价值(类似于您已经可以执行
编辑:解决方案-感谢Nick和Bzim-对于其他读者,我将根据自己的理解提供完整的解决方案:edit2:此解决方案要求foo公开公共默认构造函数。对于一个偶数stubiderbetter solution that does not required this see the very bottom of this post.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | public class Bar {} public class SomeWeirdClass<T> where T : IConvertibleFromBar<T>, new() { public T BarMaker(Bar b) { T t = new T(); t.Convert(b); return t; } } public interface IConvertibleFromBar<T> { T Convert(Bar b); } public class Foo : IConvertibleFromBar<Foo> { 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<Foo> weird = new SomeWeirdClass<Foo>(); Foo f = weird.BarMaker(b); } } |
edit2:解决方案2:创建要使用的类型转换器工厂:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | #region library defined code public class Bar {} public class SomeWeirdClass<T, TFactory> where TFactory : IConvertorFactory<Bar, T>, new() { private static TFactory convertor = new TFactory(); public T BarMaker(Bar b) { return convertor.Convert(b); } } public interface IConvertorFactory<TFrom, TTo> { TTo Convert(TFrom from); } #endregion #region user defined code public class BarToFooConvertor : IConvertorFactory<Bar, Foo> { 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<Foo, BarToFooConvertor> weird = new SomeWeirdClass<Foo, BarToFooConvertor>(); Foo f = weird.BarMaker(b); } } |
听起来你找到了解决这个大问题的办法。要回答您的特定问题:不,C和CLR都不支持"向后"泛型类型参数约束。也就是说,
1 | class C<T> where Foo : T |
"t必须是foo或不支持foo转换为"的类型。
有些语言有这样的约束;IIRC scala就是这样一种语言。我怀疑这个特性对于反变体接口的某些使用是很方便的。
我认为在语言中不一定有一种在语法上很酷的方法可以做到这一点。解决问题的一个可能的方法是定义一个可转换的接口:
1 2 3 4 5 |
那么你的班级可以是:
1 2 3 | public class Foo : IConvertible<Bar> { } |
我想这能让你接近你想去的地方…你的问题中所有的foo和bar有时会让你很难确定你的意图到底是什么。希望这有帮助。
编辑:添加了Where约束…您可能必须能够在可转换类中创建一个新实例。
编辑2:使foo继承
与其经历定义接口和修改类来实现接口的麻烦,为什么不直接这样做呢?
1 2 3 4 5 6 7 8 9 | public class SomeWeirdClass<T> { // aside: why is this method called 'BarMaker' if it returns a T? public T BarMaker(Bar b, Func<Bar, T> converter) { // ... play with b return converter(b); } } |
然后,如果您处理的对象类型为
1 2 3 |
顺便说一下(由于.NET 3.5中出现了
您可以通过用作类型约束的接口绕行。
例如,