【发布时间】:2021-10-19 14:48:10
【问题描述】:
我正在尝试弄清楚如何构建接口的通用结构以使用流畅的表示法。
我正在尝试如下结构:
public interface IGeneric<out T>
where T : IGeneric<T>
{
T Foo1();
T Foo2();
T Foo3();
}
public interface ISpecific_1 : IGeneric<ISpecific_1>
{ }
public interface ISpecific_2 : IGeneric<ISpecific_2>
{
ISpecific_2 Bar1();
ISpecific_2 Bar2();
ISpecific_2 Bar3();
}
public abstract class GenericImpl<T> : IGeneric<T>
where T : IGeneric<T>
{
public T Foo1()
{
//Do things
return (T)(object)this;
}
public T Foo2()
{
//Do things
return (T)(object)this;
}
public T Foo3()
{
//Do things
return (T)(object)this;
}
}
public class SpecificImpl1 : GenericImpl<ISpecific_1>, ISpecific_1
{
}
public class SpecificImpl2 : GenericImpl<ISpecific_2>, ISpecific_2
{
public ISpecific_2 Bar1()
{
//Do things
return this;
}
public ISpecific_2 Bar2()
{
//Do things
return this;
}
public ISpecific_2 Bar3()
{
//Do things
return this;
}
}
IGeneric 只接受它自己的实现作为 T,以确保 Foo1()、Foo2() 和 Foo3() 将返回正确的流利表示法类型。
GenericImpl 是抽象的(不是强制性的,但我想让它们使用特定的类)并实现 IGeneric。
ISpecific_1 使用自己的类型实现 IGeneric(这意味着 Generic 类中的 Foos 方法将返回 ISpecific_1)。
Specific_1 实现了 ISpecific_1 并扩展了 GenericImpl
ISpecific_2 和 Specific_2 相同,除了一些 Bars 额外方法。
这似乎可行,因为我可以这样做:
ISpecific_1 spec1 = new Specific_1();
spec1.Foo1().Foo2().Foo3(); //Everyone returns ISpecific_1 that extends the Generic class with the Foos
ISpecific_2 spec2 = new Specific_2();
spec2.Bar1().Foo1().Bar2().Foo2().Bar3().Foo3(); //Everyone returns ISpecific_2 with the Bars. Also the Generic Foos methods are available.
虽然我不能这样做:
ISpecific_1 spec1 = new Specific_1();
spec1.Bar1().Bar2().Bar3(); //ISpecific_1 nor IGeneric don't declare Bars methods
现在是问题:
第一个: 在 Foos 方法中,我必须使用手动转换返回值
return (T)(object)this;
我也可以制作 T : 类并使用
return this as T;
他们是安全的还是我错过了一个演员失败的案例?
第二个: 有没有更好的方法来做到这一点?
提前谢谢你
【问题讨论】:
-
“IGeneric 只接受它自己的实现作为 T” - 我不相信这是可能的,并且可能会导致循环依赖!
-
这被称为奇怪的重复模板模式,你可以做类似
public class MyThing : GenericImpl<ISpecific_2>这样的事情,所以现在T不会是MyThing,但因为ISpecific_2是@987654330 @ 所以请注意,它不能保证泛型类型正是实现类的类型。 -
@phuzi 一开始我也是这么想的,当我发现我错了我也很惊讶。
-
@juharr 好点,没想到这一点。谢谢!
-
问题中的代码是安全的,因为它会始终失败(因为 GenericImpl 永远不会是 T) - 请澄清问题,如果它是您所询问的安全类型
标签: c# generics interface casting fluent