我正在使用.NET框架,我真的想能够制作一个自定义类型的页面,让我的整个网站都使用它。但当我尝试从控件中访问该页面时出现问题。我希望能够返回我的特定类型的页面而不是默认页面。有没有办法做到这一点?
public class MyPage : Page
{
// My own logic
}
public class MyControl : Control
{
public MyPage Page { get; set; }
}
我正在使用.NET框架,我真的想能够制作一个自定义类型的页面,让我的整个网站都使用它。但当我尝试从控件中访问该页面时出现问题。我希望能够返回我的特定类型的页面而不是默认页面。有没有办法做到这一点?
public class MyPage : Page
{
// My own logic
}
public class MyControl : Control
{
public MyPage Page { get; set; }
}
更新:此答案编写于2011年。经过20年的人们提议将C#引入返回类型协变,现在已经实现了。请参见https://devblogs.microsoft.com/dotnet/c-9-0-on-the-record/.
看起来你想要的是返回类型协变(return type covariance)。但是,C#不支持返回类型协变。
返回类型协变是指覆盖一个基类方法,该方法返回一个较不具体的类型,而使用一个返回更具体类型的方法:
abstract class Enclosure
{
public abstract Animal Contents();
}
class Aquarium : Enclosure
{
public override Fish Contents() { ... }
}
这样是安全的,因为通过Enclosure消费Contents的用户期望一个动物,而Aquarium承诺不仅满足该要求,而且还做出更严格的承诺:该动物始终是一条鱼。
C#不支持这种协变,并且很可能永远不会支持。 CLR也不支持它。(C ++以及CLR上的C ++ / CLI实现支持它;它通过生成建议下面的类似神奇的辅助方法来支持。)
(一些语言也支持形式参数类型逆变——您可以用接受Fish的方法覆盖接受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的东西时获得更强的类型安全性。
在接口方面,我通过显式实现接口来解决它:
public interface IFoo {
IBar Bar { get; }
}
public class Foo : IFoo {
Bar Bar { get; set; }
IBar IFoo.Bar => Bar;
}
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();
}
public new MyPage Page {get return (MyPage)Page; set;}'
你无法覆盖该属性,因为它返回了不同的类型...但你可以重新定义它。
在这个例子中,你不需要协变,因为它相对简单。你所做的就是从MyPage
继承基本对象Page
。任何想要返回MyPage
而不是Page
的Control
都需要重新定义Control
的Page
属性。
是的,它支持协变性,但这取决于您要实现的确切目标。
我也倾向于经常使用泛型,这意味着当您执行以下操作时:
class X<T> {
T doSomething() {
}
}
class Y : X<Y> {
Y doSomethingElse() {
}
}
var Y y = new Y();
y = y.doSomething().doSomethingElse();
不要“丢失”你的类型。
您可以通过沿着父级树向上遍历来从任何控件访问您的页面。
myParent = this;
while(myParent.parent != null)
myParent = myParent.parent;
*未编译或测试。
或在当前上下文中获取父页面(取决于您的版本)。
YourPageType myPage = (YourPageType)yourControl.Page;
var myContextPage = this.Page as IMyContextGetter;
if(myContextPage != null)
var myContext = myContextPage.GetContext();
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; } }
}