C# 支持返回类型协方差吗?
我正在使用 .NET 框架,我真的希望能够制作我的所有网站都使用的自定义类型的页面。当我尝试从控件访问页面时,问题就出现了。我希望能够返回特定类型的页面而不是默认页面。有什么办法可以做到这一点吗?
public class MyPage : Page
{
// My own logic
}
public class MyControl : Control
{
public MyPage Page { get; set; }
}
I'm working with the .NET framework and I really want to be able to make a custom type of page that all of my website uses. The problem comes when I am trying to access the page from a control. I want to be able to return my specific type of page instead of the default page. Is there any way to do this?
public class MyPage : Page
{
// My own logic
}
public class MyControl : Control
{
public MyPage Page { get; set; }
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
更新:这个答案写于 2011 年。经过二十年人们提出 C# 返回类型协方差,他们已经实现了。请参阅 https://devblogs.microsoft.com/ 中的协变返回dotnet/c-9-0-on-the-record/。
听起来你想要的是返回类型协方差。 C# 不支持返回类型协方差。
返回类型协变是指用返回更具体类型的基类方法覆盖返回不太具体类型的基类方法:
这是安全的,因为通过 Enclosure 的内容消费者期望动物,而 Aquarium 承诺不仅满足该要求,但此外,还要做出更严格的承诺:该动物始终是鱼。
C# 不支持这种协方差,而且不太可能永远支持。 CLR 不支持它。 (它受到 C++ 以及 CLR 上的 C++/CLI 实现的支持;它通过生成我在下面建议的那种神奇的辅助方法来实现这一点。)
(某些语言也支持形式参数类型逆变——您可以覆盖它一个接受 Fish 的方法和一个接受 Animal 的方法同样得到满足;基类要求处理任何 Fish,并且派生类承诺不仅处理鱼,还处理任何动物。 CLR不支持形式参数类型逆变。)
解决此限制的方法是执行以下操作:
现在,您既可以获得重写虚拟方法的好处,又可以在使用编译时类型 Aquarium 时获得更强的打字能力。
UPDATE: This answer was written in 2011. After two decades of people proposing return type covariance for C# they have been implemented. See Covariant Returns in https://devblogs.microsoft.com/dotnet/c-9-0-on-the-record/.
It sounds like what you want is return type covariance. C# does not support return type covariance.
Return type covariance is where you override a base class method that returns a less-specific type with one that returns a more specific type:
This is safe because consumers of Contents via Enclosure expect an Animal, and Aquarium promises to not only fulfill that requirement, but moreover, to make a more strict promise: that the animal is always a fish.
This kind of covariance is not supported in C#, and is unlikely to ever be supported. It is not supported by the CLR. (It is supported by C++, and by the C++/CLI implementation on the CLR; it does so by generating magical helper methods of the sort I suggest below.)
(Some languages support formal parameter type contravariance as well -- that you can override a method that takes a Fish with a method that takes an Animal. Again, the contract is fulfilled; the base class requires that any Fish be handled, and the derived class promises to not only handle fish, but any animal. Similarly, C# and the CLR do not support formal parameter type contravariance.)
The way you can work around this limitation is to do something like:
Now you get both the benefits of overriding a virtual method, and getting stronger typing when using something of compile-time type Aquarium.
通过接口,我通过显式实现接口来解决这个问题:
With interfaces I got around it by explicitly implementing the interface:
这是即将推出的 C# 9.0 (.Net 5) 的一项功能您现在可以下载预览版本。
以下代码现在可以成功构建(不会给出:
错误 CS0508: 'Tiger.GetFood()': 返回类型必须是 'Food' 才能匹配重写成员 'Animal.GetFood()'
)This is a feature for the upcoming C# 9.0 (.Net 5) of which you can download a preview version now.
The following code now builds successfully (without giving:
error CS0508: 'Tiger.GetFood()': return type must be 'Food' to match overridden member 'Animal.GetFood()'
)将其放置在 MyControl 对象中是可行的:
您无法覆盖该属性,因为它返回不同的类型...但您可以重新定义它。
在此示例中不需要协方差,因为它相对简单。您所做的就是从
MyPage
继承基础对象Page
。任何想要返回MyPage
而不是Page
的Control
都需要重新定义的
Page
属性控制Placing this in the MyControl object would work:
You can't override the property because it returns a different type... but you can redefine it.
You don't need covariance in this example, since it is relatively simple. All you're doing is inheriting the base object
Page
fromMyPage
. AnyControl
that you want to returnMyPage
instead ofPage
needs to redefine thePage
property of theControl
是的,它支持协方差,但这取决于您想要实现的具体目标。
我也倾向于大量使用泛型,这意味着当您执行以下操作时:
并且不会“丢失”您的类型。
Yes, it supports covariance, but it depends upon the exact thing you are trying to achieve.
I also tend to use generics a lot for things, which means that when you do something like:
And not "lose" your types.
我没试过,但这不行吗?
I haven't tried it, but doesn't this work?
是的。有多种方法可以做到这一点,这只是一种选择:
您可以使您的页面实现一些自定义接口,该接口公开一个名为“GetContext”或其他方法的方法,并且它返回您的特定信息。然后您的控件可以简单地请求页面并进行转换:
然后您可以根据需要使用该上下文。
Yes. There are multiple ways of doing this, and this is just one option:
You can make your page implement some custom interface that exposes a method called "GetContext" or something, and it returns your specific information. Then your control can simply request the page and cast:
Then you can use that context however you wish.
您可以通过遍历父树从任何控件访问您的页面。即
* 没有编译或测试。
或者获取当前上下文中的父页面(取决于您的版本)。
然后我喜欢做的是这样的:我创建一个界面,其中包含我想在控件中使用的功能(例如 IHostingPage)
然后我投射父页面 'IHostingPage host = (IHostingPage)Parent;'我已准备好从我的控制中调用我需要的页面上的函数。
You can access your page from any control by walking up the parent tree. That is
*Did not compile or test.
Or get the parent page in the current context (depends on your version).
Then what I like to do is this: I create an interface with the functions I want to use in the control (for example IHostingPage)
Then I cast the parent page 'IHostingPage host = (IHostingPage)Parent;' and I am all set to call the function on the page I need from my control.
我会这样做:
I will do it in this way: