我正在使用.NET框架,但我确实希望能够制作一种供我所有网站使用的自定义类型的页面。当我尝试从控件访问页面时,问题就来了。我希望能够返回我的特定页面类型而不是默认页面。有什么办法吗?

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

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


评论

利用泛型可能复制c#协变量返回类型

#1 楼


UPDATE:该答案写于2011年。经过二十多年的人们提出C#的返回类型协方差后,看起来终于可以实现了。我很惊讶。有关公告,请参见https://devblogs.microsoft.com/dotnet/welcome-to-c-9-0/的底部;我相信细节会在后面。


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

返回类型协方差是您重写基类方法的方法,该方法返回的是不那么特定的类型,而返回的是更具体的类型:

abstract class Enclosure
{
    public abstract Animal Contents();
}
class Aquarium : Enclosure
{
    public override Fish Contents() { ... }
}


这是安全的,因为通过附件访问内容的消费者希望有动物,而水族馆承诺不仅要满足这一要求,而且要做出更严格的承诺:动物永远是鱼。

C#不支持这种协方差,而且不太可能得到支持。 CLR不支持它。 (它受C ++和CLR上C ++ / CLI实现的支持;它是通过生成我在下面建议的那种神奇的辅助方法来实现的。)

(某些语言支持形式参数类型对数同样,您也可以使用采用Animal的方法来覆盖采用Fish的方法;同样,合同也应得到履行;基类要求处理任何Fish,派生类承诺不仅要处理鱼类,但是,任何动物都一样。C#和CLR不支持形式参数类型的自变量。)

解决此限制的方法是执行以下操作:

现在,您既具有覆盖虚拟方法的好处,又具有使用编译时类型的Aquarium时获得更强类型的好处。

评论


完善!我忘了躲藏!

–凯尔·斯莱顿(Kyle Sletten)
11年4月18日在21:38

一如既往的好答案。我们错过了这些答案已有一段时间了。

–德南杰(Dhananjay)
2012年7月20日13:32

是否有理由不支持可在任何地方读取的返回协方差和参数协方差?

– porges
2012年11月14日下午5:19

@EricLippert作为C#分组用户,我一直对“后台”感到好奇,所以我只想问:C#是否考虑过返回类型协方差并且认为ROI太低?我了解时间和bugtet的限制,但是从您的“不可能得到支持”的说法看来,设计团队实际上宁愿不使用该语言。另一方面,Java在Java 5中引入了该语言功能,所以我想知道控制语言设计过程的更大,总体原则之间有什么区别。

–克里斯蒂安·迪亚科内斯库(Cristian Diaconescu)
2012年11月20日10:14

@Cyral:正确;它已列在“中等利率”类别中-这已经有很长时间了!可能会发生,但是我非常怀疑。

–埃里克·利珀特
2015年5月1日23:50

#2 楼

通过接口,我通过显式实现接口来解决它:

public interface IFoo {
  IBar Bar { get; }
}
public class Foo : IFoo {
  Bar Bar { get; set; }
  IBar IFoo.Bar => Bar;
}


#3 楼

将其放置在MyControl对象中将起作用:

 public new MyPage Page {get return (MyPage)Page; set;}'


您无法覆盖该属性,因为它返回了不同的类型...但是您可以重新定义它。

在此示例中,您不需要协方差,因为它相对简单。您要做的就是从Page继承基础对象MyPage。您要返回Control而不是MyPage的任何Page需要重新定义PageControl属性

#4 楼

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

我也经常在泛型上使用泛型,这意味着当您执行以下操作时:

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

}

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

var Y y = new Y();
y = y.doSomething().doSomethingElse();


不要“丢掉”您的类型。

#5 楼

这是即将发布的C#9.0(.Net 5)的一项功能,您可以立即下载其预览版本。
以下代码现已成功构建(无提示:error CS0508: 'Tiger.GetFood()': return type must be 'Food' to match overridden member 'Animal.GetFood()'
 class Food { }
class Meat : Food { }

abstract class Animal {
    public abstract Food GetFood();
}

class Tiger : Animal {
    public override Meat GetFood() => default;
}

class Program {
    static void Main() => new Tiger();
}
 


#6 楼

我还没有尝试过,但是没有用吗?

YourPageType myPage = (YourPageType)yourControl.Page;


评论


是的,确实如此。我只想尝试避免在整个地方投射。

–凯尔·斯莱顿(Kyle Sletten)
2011年4月18日在21:28

#7 楼

是。有多种方法可以执行此操作,而这只是一个选择:

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

#8 楼

您可以通过上级父树从任何控件访问页面。那就是

myParent = this;

while(myParent.parent != null)
  myParent = myParent.parent;


*没有编译或测试。

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


然后我想要做的是:我创建一个接口,该接口带有要在控件中使用的功能(例如IHostingPage)。

然后我强制转换父页面“ IHostingPage host =(IHostingPage)Parent;”而且我已经准备好从控件中调用所需页面上的函数。

#9 楼

我将以这种方式进行操作:

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; } }
}