假设我用以下内容编写了一个库:
public class Bar { /* ... */ }
public class SomeWeirdClass<T>
where T : ???
{
public T BarMaker(Bar b)
{
// ... play with b
T t = (T)b
return (T) b;
}
}
Run Code Online (Sandbox Code Playgroud)
后来,我希望用户通过定义他们自己的类型来使用我的库,这些类型可以转换为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<Foo> weird = new SomeWeirdClass<Foo>();
Foo f = weird.BarMaker(b);
}
}
Run Code Online (Sandbox Code Playgroud)
这将编译,如果我设置,where T : Foo但问题是我不知道在库的编译时Foo,我实际上想要更像where T : some class that can be instantiated, given a Bar
这可能吗?从我有限的知识来看,它似乎并不是,但.NET框架及其用户的独创性总让我惊喜......
这可能与静态接口方法的概念有关- 至少,我可以看到能够指定工厂方法的存在以创建对象的价值(类似于您已经可以执行的方式where T : new())
编辑:解决方案 - 感谢Nick和bzIm - 对于其他读者,我将提供一个完整的解决方案,因为我理解它:edit2:此解决方案需要Foo公开一个公共默认构造函数.对于一个甚至更愚蠢的更好的解决方案,不需要这个看到这篇文章的最底层.
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);
}
}
Run Code Online (Sandbox Code Playgroud)
edit2:解决方案2:创建一个类型转换器工厂使用:
#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);
}
}
Run Code Online (Sandbox Code Playgroud)
听起来您找到了解决更大问题的解决方案。要回答您的特定问题:不,C#和CLR都不支持“向后”通用类型参数约束。那是,
class C<T> where Foo : T
Run Code Online (Sandbox Code Playgroud)
不支持“ T必须是Foo或Foo转换为的类型”。
有些语言有这种约束。IIRC Scala是这种语言。我怀疑此功能对于逆接口的某些用法会很方便。
我认为语言中不一定有一种语法上很酷的方法来做到这一点。解决您的问题的一种可能的解决方案是定义一个可转换接口:
public interface IConvertible<T>
where T : new() // Probably will need this
{
T Convert();
}
Run Code Online (Sandbox Code Playgroud)
那么你的课程可能是:
public class Foo : IConvertible<Bar>
{
}
Run Code Online (Sandbox Code Playgroud)
我认为这让你接近你想要的地方......你的问题中的所有 Foo 和 Bar 有时会让你很难准确地确定你的意图是什么。希望这可以帮助。
编辑:添加了 where 约束...您可能必须能够在可转换类中创建一个新实例。
编辑2:使 Foo 继承自ICovertible<Bar>