是否可以为类型参数指定一个通用约束,以便从另一个类型转换?

假设我用以下内容编写了一个库:

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参数。