C#是否支持返回types协变?
我正在与.NET框架,我真的想能够使我的网站使用的自定义types的页面。 当我试图从控件访问页面时出现问题。 我希望能够返回我的特定types的页面,而不是默认页面。 有没有办法做到这一点?
public class MyPage : Page { // My own logic } public class MyControl : Control { public MyPage Page { get; set; } }
这听起来像你想要的是返回types的协方差。 C#不支持返回types协方差。
返回types协方差是您重写一个基类方法的地方,该方法返回一个较不具体的types,并返回一个更具体的types:
abstract class Enclosure { public abstract Animal Contents(); } class Aquarium : Enclosure { public override Fish Contents() { ... } }
这是安全的,因为消费者通过shell期望一种动物,而水族馆承诺不仅满足这个要求,而且要做出更严格的承诺:动物总是鱼。
C#不支持这种协方差,不太可能被支持。 CLR不支持它。 (它由C ++和CLR上的C ++ / CLI实现支持;它通过产生下面我build议的那种神奇的帮助方法来实现)。
(有些语言也支持forms参数types的反变换 – 你可以重写一个方法,该方法需要一个带有一个Animal的方法,同样的,契约被满足了;基类需要处理任何的Fish,派生类承诺不仅处理鱼,而且处理任何动物。同样,C#和CLR也不支持forms参数types的变换。)
您可以解决此限制的方法是执行如下操作:
abstract class Enclosure { protected abstract Animal GetContents(); public Animal Contents() { return this.GetContents(); } } class Aquarium : Enclosure { protected override Animal GetContents() { return this.Contents(); } public new Fish Contents() { ... } }
现在,你可以同时获得重写虚拟方法的好处,并且在使用某种编译时types的Aquarium时可以获得更强的input。
放置在MyControl对象将工作:
public new MyPage Page {get return (MyPage)Page; set;}'
你不能重写属性,因为它返回一个不同的types…但你可以重新定义它。
在这个例子中,你不需要协方差,因为它比较简单。 你所做的只是inheritanceMyPage
的基础对象Page
。 任何想要返回MyPage
而不是Page
的Control
需要重新定义Control
的Page
属性
本博文系列详细讨论了返回types协方差的问题,甚至谈到如何用IL来做到这一点。
http://www.simple-talk.com/community/blogs/simonc/archive/2010/07/14/93495.aspx
http://www.simple-talk.com/community/blogs/simonc/archive/2010/07/16/93516.aspx
http://www.simple-talk.com/community/blogs/simonc/archive/2010/07/19/93562.aspx
对不起,只是张贴链接,但它是非常详细的,在这里引用片段不会有帮助。 它显示了如何使用IL代码实现。
是的,它支持协变性,但它取决于你想要实现的确切的事情。
我也倾向于使用generics很多的东西,这意味着当你做这样的事情时:
class X<T> { T doSomething() { } } class Y : X<Y> { Y doSomethingElse() { } } var Y y = new Y(); y = y.doSomething().doSomethingElse();
而不是“失去”你的types。
我没有尝试过,但是这不工作?
YourPageType myPage = (YourPageType)yourControl.Page;
是。 有多种方法可以做到这一点,这只是一个select:
你可以让你的页面实现一些自定义的接口,它暴露了一个名为“GetContext”的方法,它返回你的具体信息。 然后你的控制可以简单地请求页面并施放:
var myContextPage = this.Page as IMyContextGetter; if(myContextPage != null) var myContext = myContextPage.GetContext();
然后,您可以使用该上下文,但是你希望。
您可以通过在父树上走,从任何控件访问您的页面。 那是
myParent = this; while(myParent.parent != null) myParent = myParent.parent;
*没有编译或testing。
或者在当前上下文中获取父页面(取决于您的版本)。
那么我喜欢做的是这样的:我创build一个接口与我想在控制中使用的function(例如IHostingPage)
然后我投了父页“IHostingPage主机=(IHostingPage)父;” 而且我都准备在我需要的页面上调用这个函数。
我会这样做:
class R { public int A { get; set; } } class R1: R { public int B { get; set; } } class A { public RX { get; set; } } class B : A { private R1 _x; public new R1 X { get => _x; set { ((A)this).X = value; _x = value; } } }
有了接口,我明白地实现了接口:
public interface IFoo { IBar Bar { get; } } public class Foo : IFoo { Bar Bar { get; set; } IBar IFoo.Bar => Bar; }