Does C# support return type covariance?
C#CovarianceC# Problem Overview
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; }
}
C# Solutions
Solution 1 - C#
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:
abstract class Enclosure
{
public abstract Animal Contents();
}
class Aquarium : Enclosure
{
public override Fish Contents() { ... }
}
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:
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() { ... }
}
Now you get both the benefits of overriding a virtual method, and getting stronger typing when using something of compile-time type Aquarium.
Solution 2 - C#
With interfaces I got around it by explicitly implementing the interface:
public interface IFoo {
IBar Bar { get; }
}
public class Foo : IFoo {
Bar Bar { get; set; }
IBar IFoo.Bar => Bar;
}
Solution 3 - C#
Placing this in the MyControl object would work:
public new MyPage Page {get return (MyPage)Page; set;}'
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
from MyPage
. Any Control
that you want to return MyPage
instead of Page
needs to redefine the Page
property of the Control
Solution 4 - C#
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:
class X<T> {
T doSomething() {
}
}
class Y : X<Y> {
Y doSomethingElse() {
}
}
var Y y = new Y();
y = y.doSomething().doSomethingElse();
And not "lose" your types.
Solution 5 - C#
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()'
)
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();
}
Solution 6 - C#
You can access your page from any control by walking up the parent tree. That is
myParent = this;
while(myParent.parent != null)
myParent = myParent.parent;
*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.
Solution 7 - C#
I haven't tried it, but doesn't this work?
YourPageType myPage = (YourPageType)yourControl.Page;
Solution 8 - C#
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:
var myContextPage = this.Page as IMyContextGetter;
if(myContextPage != null)
var myContext = myContextPage.GetContext();
Then you can use that context however you wish.
Solution 9 - C#
I will do it in this way:
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; } }
}