O suporte ao C # retorna a covariância do tipo?

Eu estou trabalhando com o .NET framework e eu realmente quero ser capaz de fazer um tipo personalizado de página que todo o meu site usa. O problema surge quando estou tentando acessar a página de um controle. Eu quero ser capaz de retornar meu tipo específico de página em vez da página padrão. Há alguma maneira de fazer isso?

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

Parece que o que você quer é a covariância do tipo de retorno. C # não suporta a covariância do tipo de retorno.

A covariância do tipo de retorno é onde você substitui um método de class base que retorna um tipo menos específico por um que retorna um tipo mais específico:

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

Isso é seguro porque os consumidores de Conteúdo via Enclosure esperam que um Animal e um Aquário prometam não apenas cumprir esse requisito, mas, além disso, fazer uma promise mais rigorosa: que o animal é sempre um peixe.

Esse tipo de covariância não é suportado em C # e é improvável que seja suportado. Não é suportado pelo CLR. (Ele é suportado pelo C ++ e pela implementação de C ++ / CLI no CLR; ele faz isso gerando methods auxiliares mágicos do tipo que eu sugiro abaixo.)

(Algumas linguagens também suportam contravarança formal de parâmetro – você pode sobrescrever um método que usa Fish com um método que usa um Animal. Novamente, o contrato é cumprido; a class base requer que qualquer peixe seja manipulado, e o derivado A class promete não apenas lidar com peixes, mas com qualquer animal. Da mesma forma, C # e o CLR não suportam contravariância formal do tipo de parâmetro.)

A maneira como você pode contornar essa limitação é fazer 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() { ... } } 

Agora, você obtém os benefícios de replace um método virtual e obter uma digitação mais forte ao usar algo do tipo Aquário em tempo de compilation.

Colocar isso no object MyControl funcionaria:

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

Você não pode replace a propriedade porque ela retorna um tipo diferente … mas você pode redefini-la.

Você não precisa de covariância neste exemplo, já que é relativamente simples. Tudo o que você está fazendo é herdar a Page do object base do MyPage . Qualquer Control que você deseja retornar MyPage vez de Page precisa redefinir a propriedade de página do Control

Esta série de posts discute detalhadamente a questão da covariância do tipo de retorno, mesmo falando sobre como fazer isso com o 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

Desculpe por apenas postar links, mas é bastante detalhado e citar trechos aqui não seria tão útil. Mostra como isso pode ser alcançado usando o código IL.

Sim, ele suporta a covariância, mas depende da coisa exata que você está tentando alcançar.

Eu também tendem a usar muito genéricos para as coisas, o que significa que quando você faz algo como:

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

E não “perder” seus tipos.

Com as interfaces, contornei explicitamente implementando a interface:

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

Eu não tentei, mas isso não funciona?

 YourPageType myPage = (YourPageType)yourControl.Page; 

Sim. Existem várias maneiras de fazer isso, e isso é apenas uma opção:

Você pode fazer com que sua página implemente alguma interface personalizada que expõe um método chamado “GetContext” ou algo assim, e retorna suas informações específicas. Então seu controle pode simplesmente solicitar a página e lançar:

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

Então você pode usar esse contexto como quiser.

Você pode acessar sua página a partir de qualquer controle, percorrendo a tree pai. Isso é

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

* Não compilou nem testou.

Ou obtenha a página pai no contexto atual (depende da sua versão).


Então o que eu gosto de fazer é isto: Eu crio uma interface com as funções que eu quero usar no controle (por exemplo, IHostingPage)

Então eu lancei a página pai ‘IHostingPage host = (IHostingPage) Parent;’ e estou pronto para chamar a function na página que eu preciso do meu controle.

Eu farei assim:

 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; } } }