我正在使用.NET框架,我真的希望能够创建一个我所有网站都使用的自定义类型的页面。当我尝试从控件访问页面时出现问题。我希望能够返回我的特定类型的页面而不是默认页面。有没有办法做到这一点?
public class MyPage : Page
{
// My own logic
}
public class MyControl : Control
{
public MyPage Page { get; set; }
}
答案 0 :(得分:145)
听起来你想要的是返回类型协方差。 C#不支持返回类型协方差。
返回类型协方差是覆盖基类方法的地方,该方法返回一个不太具体的类型,返回更具体的类型:
abstract class Enclosure
{
public abstract Animal Contents();
}
class Aquarium : Enclosure
{
public override Fish Contents() { ... }
}
这是安全的,因为通过Enclosure的内容消费者期望动物,而水族馆承诺不仅要满足这个要求,而且要做出更严格的承诺:动物总是鱼。
C#不支持这种协方差,也不太可能支持。 CLR不支持它。 (它受C ++和CLR上的C ++ / CLI实现支持;它通过生成我在下面建议的类型的魔法辅助方法来实现。)
(有些语言也支持形式参数类型的逆转 - 您可以覆盖一个方法,该方法使用带有Animal的方法接受Fish。同样,合同完成;基类要求处理任何Fish,并且派生类不仅承诺处理鱼类,而且承诺任何动物。同样,C#和CLR不支持形式参数类型的逆转。)
解决此限制的方法是执行以下操作:
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() { ... }
}
现在,您可以获得覆盖虚拟方法的好处,并在使用编译时类型的Aquarium时获得更强的输入。
答案 1 :(得分:2)
将其置于MyControl对象中将起作用:
public new MyPage Page {get return (MyPage)Page; set;}'
您无法覆盖该属性,因为它返回不同的类型......但您可以重新定义它。
在这个例子中你不需要协方差,因为它相对简单。您所做的只是从Page
继承基础对象MyPage
。您要返回Control
而不是MyPage
的任何Page
都需要重新定义Page
的{{1}}属性
答案 2 :(得分:2)
此博客文章系列详细讨论了返回类型协方差的问题,甚至讨论了如何使用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代码实现它。
答案 3 :(得分:2)
使用接口我通过显式实现接口来解决它:
public interface IFoo {
IBar Bar { get; }
}
public class Foo : IFoo {
Bar Bar { get; set; }
IBar IFoo.Bar => Bar;
}
答案 4 :(得分:1)
是的,它支持协方差,但这取决于你想要达到的确切目标。
我也倾向于对事物使用泛型,这意味着当你做类似的事情时
class X<T> {
T doSomething() {
}
}
class Y : X<Y> {
Y doSomethingElse() {
}
}
var Y y = new Y();
y = y.doSomething().doSomethingElse();
而不是“失去”你的类型。
答案 5 :(得分:1)
这是upcoming C# 9.0 (.Net 5)的一项功能,您现在可以download a preview version。
以下代码现在可以成功构建(不提供: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 :(得分:0)
您可以通过向上走父树来从任何控件访问您的页面。那是
myParent = this;
while(myParent.parent != null)
myParent = myParent.parent;
*没有编译或测试。
或者在当前上下文中获取父页面(取决于您的版本)。
然后我喜欢做的是:我创建一个包含我想在控件中使用的函数的接口(例如IHostingPage)
然后我转换父页面'IHostingPage host =(IHostingPage)Parent;'我已准备好从我的控制中调用我需要的页面上的功能。
答案 7 :(得分:0)
我没有尝试过,但这不起作用吗?
YourPageType myPage = (YourPageType)yourControl.Page;
答案 8 :(得分:0)
是。有多种方法可以做到这一点,这只是一个选择:
您可以使您的页面实现一些自定义接口,该接口公开名为“GetContext”的方法或其他东西,并返回您的特定信息。然后你的控件可以简单地请求页面和演员:
var myContextPage = this.Page as IMyContextGetter;
if(myContextPage != null)
var myContext = myContextPage.GetContext();
然后您可以根据自己的意愿使用该上下文。
答案 9 :(得分:0)
我会这样做:
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; } }
}