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而不是PageControl需要重新定义ControlPage属性

本博文系列详细讨论了返回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; }