¿Apoya C # la covarianza de tipo de retorno?

Estoy trabajando con .NET Framework y realmente quiero poder crear un tipo de página personalizado que use todo mi sitio web. El problema surge cuando bash acceder a la página desde un control. Deseo poder devolver mi tipo específico de página en lugar de la página predeterminada. ¿Hay alguna manera de hacer esto?

public class MyPage : Page { // My own logic } public class MyControl : Control { public MyPage Page { get; set; } } 

Parece que lo que quieres es devolver la covarianza tipo. C # no es compatible con la covarianza de tipo de retorno.

La covarianza del tipo de devolución es donde anula un método de clase base que devuelve un tipo menos específico con uno que devuelve un tipo más específico:

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

Esto es seguro porque los consumidores de Contents via Enclosure esperan que Animal, y Aquarium prometan no solo cumplir ese requisito, sino también hacer una promesa más estricta: que el animal siempre es un pez.

Este tipo de covarianza no es compatible con C # y es poco probable que alguna vez sea compatible. No es compatible con CLR. (Es compatible con C ++ y con la implementación de C ++ / CLI en el CLR; lo hace generando métodos de ayuda mágica del tipo que sugiero a continuación).

(Algunos lenguajes también admiten la contravariancia del tipo de parámetro formal, que puede anular un método que toma un pez con un método que toma un animal. De nuevo, se cumple el contrato; la clase base requiere que se maneje cualquier pez, y el derivado la clase promete no solo manipular peces, sino cualquier animal. Del mismo modo, C # y CLR no son compatibles con la contravariancia del tipo de parámetro formal).

La forma en que puede evitar esta limitación es hacer algo como:

 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() { ... } } 

Ahora obtienes los beneficios de anular un método virtual y obtener una escritura más fuerte cuando utilizas algo del tipo Acuario en tiempo de comstackción.

Colocar esto en el objeto MyControl funcionaría:

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

No puede anular la propiedad porque devuelve un tipo diferente … pero puede redefinirla.

No necesita covarianza en este ejemplo, ya que es relativamente simple. Todo lo que está haciendo es heredar la Page del objeto base desde MyPage . Cualquier Control que desee devolver MyPage lugar de Page necesita redefinir la propiedad de página del Control

Esta serie de publicaciones de blog analiza en detalle el problema de la covarianza del tipo de devolución, e incluso habla sobre cómo hacerlo con 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

Perdón por solo publicar enlaces, pero es bastante detallado y citar fragmentos aquí no sería tan útil. Muestra cómo se puede lograr usando el código IL.

Sí, admite la covarianza, pero depende de lo exacto que intenta lograr.

También tiendo a usar generics mucho para las cosas, lo que significa que cuando haces algo como:

 class X { T doSomething() { } } class Y : X { Y doSomethingElse() { } } var Y y = new Y(); y = y.doSomething().doSomethingElse(); 

Y no “pierdas” tus tipos.

Con las interfaces lo solucioné implementando explícitamente la interfaz:

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

No lo he probado, pero ¿no funciona?

 YourPageType myPage = (YourPageType)yourControl.Page; 

Sí. Hay varias formas de hacerlo, y esta es solo una opción:

Puede hacer que su página implemente alguna interfaz personalizada que expone un método llamado “GetContext” o algo así, y devuelve su información específica. Entonces su control puede simplemente solicitar la página y el elenco:

 var myContextPage = this.Page as IMyContextGetter; if(myContextPage != null) var myContext = myContextPage.GetContext(); 

Entonces puedes usar ese contexto como quieras.

Puede acceder a su página desde cualquier control caminando por el árbol padre. Es decir

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

* No comstack ni prueba.

O bien, obtenga la página principal en el contexto actual (depende de su versión).


Entonces, lo que me gusta hacer es esto: creo una interfaz con las funciones que quiero usar en el control (por ejemplo, IHostingPage)

Luego lanzo la página principal ‘IHostingPage host = (IHostingPage) Parent;’ y estoy todo listo para llamar a la función en la página que necesito de mi control.

Lo haré de esta manera:

 class R { public int A { get; set; } } class R1: R { public int B { get; set; } } class A { public RX { get; set; } } class B : A { private R1 _x; public new R1 X { get => _x; set { ((A)this).X = value; _x = value; } } }