C#是否支持返回型协方差?

C#是否支持返回型协方差?,c#,covariance,C#,Covariance,我正在使用.NET framework,我真的希望能够创建一个所有网站都使用的自定义页面类型。当我试图从控件访问页面时,问题就出现了。我希望能够返回特定类型的页面,而不是默认页面。有没有办法做到这一点 public class MyPage : Page { // My own logic } public class MyControl : Control { public MyPage Page { get; set; } } 通过向上遍历父树,可以从任何控件访问页面。就

我正在使用.NET framework,我真的希望能够创建一个所有网站都使用的自定义页面类型。当我试图从控件访问页面时,问题就出现了。我希望能够返回特定类型的页面,而不是默认页面。有没有办法做到这一点

public class MyPage : Page
{
    // My own logic
}

public class MyControl : Control
{
    public MyPage Page { get; set; }
}

通过向上遍历父树,可以从任何控件访问页面。就是

myParent = this;

while(myParent.parent != null)
  myParent = myParent.parent;
*没有编译或测试

或者在当前上下文中获取父页面(取决于您的版本)


然后我想做的是:创建一个接口,其中包含我想在控件中使用的函数(例如IHostingPage)


然后我强制转换父页面'IHostingPage主机=(IHostingPage)父;'我已经准备好从控件中调用页面上需要的函数。

我还没有尝试过,但这不管用吗

YourPageType myPage = (YourPageType)yourControl.Page;

对。有多种方法可以做到这一点,这只是一种选择:

您可以使页面实现一些自定义接口,该接口公开一个名为“GetContext”或其他方法,并返回您的特定信息。然后,您的控件可以简单地请求页面并进行强制转换:

var myContextPage = this.Page as IMyContextGetter;

if(myContextPage != null)
   var myContext = myContextPage.GetContext();

然后,您可以根据自己的意愿使用该上下文

是的,它支持协方差,但这取决于您试图实现的确切目标

我也倾向于在很多事情上使用泛型,这意味着当你做一些事情时,比如:

class X<T> {
    T doSomething() {
    }

}

class Y : X<Y> {
    Y doSomethingElse() {
    }
}

var Y y = new Y();
y = y.doSomething().doSomethingElse();
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() { ... }
}
X类{
T doSomething(){
}
}
Y类:X类{
Y doSomethingElse(){
}
}
var Y=新的Y();
y=y.doSomething().doSomethingElse();

并且不要“丢失”您的类型。

将其放在MyControl对象中可以:

 public new MyPage Page {get return (MyPage)Page; set;}'
无法重写该属性,因为它返回不同的类型。。。但你可以重新定义它

在本例中,您不需要协方差,因为它相对简单。您所做的就是从
MyPage
继承基本对象
Page
。要返回
MyPage
而不是
Page
的任何
控件
,都需要重新定义
控件的
页面
属性


更新:这个答案写于2011年。二十年来,人们一直在为C#提出回归型协方差,但现在看来,它最终会实现;我相当惊讶。有关公告,请参见的底部;我相信细节会随之而来


听起来你想要的是返回型协方差。C#不支持返回类型协方差

返回类型协方差是指将返回不太特定类型的基类方法重写为返回更特定类型的基类方法:

abstract class Enclosure
{
    public abstract Animal Contents();
}
class Aquarium : Enclosure
{
    public override Fish Contents() { ... }
}
这是安全的,因为通过围栏的内容物的消费者期望动物,而水族馆承诺不仅要满足这一要求,而且要做出更严格的承诺:动物永远是鱼

这种协方差在C#中不受支持,而且不太可能得到支持。CLR不支持它。(它由C++支持,并且通过CLR上的C++/CLI实现;它通过生成下面我建议的类型的魔法助手方法来实现) (有些语言也支持形式参数类型逆变——你可以用一个接受动物的方法覆盖接受鱼的方法。同样,契约得到了履行;基类要求处理任何鱼,派生类承诺不仅处理鱼,而且处理任何动物。类似地,C#和CLR不支持端口形式参数类型相反。)

解决此限制的方法是:

class X<T> {
    T doSomething() {
    }

}

class Y : X<Y> {
    Y doSomethingElse() {
    }
}

var Y y = new Y();
y = y.doSomething().doSomethingElse();
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() { ... }
}

现在,您既可以获得重写虚拟方法的好处,也可以在使用编译时类型的水族箱时获得更强的键入能力。

我将这样做:

class R {
    public int A { get; set; }
}

class R1: R {
    public int B { get; set; }
}

class A
{        
    public R X { 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;
}

这是一项功能,您现在可以使用它

下面的代码现在已成功生成(未给出:
错误CS0508:'Tiger.GetFood()':返回类型必须为'Food'才能匹配重写的成员'Animal.GetFood()'

class食品{}
肉类类:食品{}
抽象类动物{
公共抽象食物获取食物();
}
虎类:动物{
public override Meat GetFood()=>默认值;
}
班级计划{
静态void Main()=>new Tiger();
}

是的,确实如此。我只是想尽量避免到处施放。太好了!我忘了隐藏!答案总是很好。我们错过了这些答案有一段时间了。有没有理由不支持返回协方差和参数反方差可以在任何地方阅读?@EricLippert作为一个C#groupie,我总是对“后台”,所以我只想问:是否考虑过C#的返回类型协方差,并认为其ROI太低?我理解时间和错误限制,但从您的“不太可能得到支持”中可以看出“听起来设计团队实际上不希望在语言中包含这一点。另一方面,Java在Java 5中引入了这一语言功能,所以我想知道在控制语言设计过程的更大、更重要的原则方面有什么不同。@Cyral:正确;它在“中等利率”桶——这是它已经存在很长一段时间的地方!它可能会发生,但我相当怀疑。可能重复