Estou trabalhando com o .NET framework e realmente quero ser capaz de fazer um tipo de página personalizado que todo o meu site use. O problema surge quando tento acessar a página de um controle. Desejo poder 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; }
}
c#
covariance
Kyle Sletten
fonte
fonte
Respostas:
ATUALIZAÇÃO: esta resposta foi escrita em 2011. Após duas décadas de pessoas propondo covariância de tipo de retorno para C #, parece que finalmente será implementado; Estou bastante surpreso. Consulte o final de https://devblogs.microsoft.com/dotnet/welcome-to-c-9-0/ para obter o anúncio; Tenho certeza de que os detalhes virão.
Parece que o que você quer é a covariância do tipo de retorno. C # não oferece suporte à covariância de tipo de retorno.
A covariância do tipo de retorno é onde você substitui um método de classe base que retorna um tipo menos específico por outro 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 um Animal, e o Aquarium promete não apenas cumprir esse requisito, mas, além disso, fazer uma promessa mais rígida: que o animal é sempre um peixe.
Esse tipo de covariância não é compatível com C # e provavelmente nunca será compatível. Não é compatível com o CLR. (É suportado por C ++ e pela implementação C ++ / CLI no CLR; ele o faz gerando métodos auxiliares mágicos do tipo que sugiro abaixo.)
(Algumas linguagens também suportam contravariância de tipo de parâmetro formal - que você pode substituir um método que pega um Peixe por um método que pega um Animal. Novamente, o contrato é cumprido; a classe base requer que qualquer Peixe seja manipulado, e o derivado classe promete não apenas lidar com peixes, mas qualquer animal. Da mesma forma, C # e o CLR não suportam contravariância de tipo de parâmetro formal.)
A maneira de 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 substituir um método virtual e obter uma digitação mais forte ao usar algo do tipo Aquarium em tempo de compilação.
fonte
Com interfaces, resolvi isso implementando explicitamente a interface:
public interface IFoo { IBar Bar { get; } } public class Foo : IFoo { Bar Bar { get; set; } IBar IFoo.Bar => Bar; }
fonte
Colocá-lo no objeto MyControl funcionaria:
public new MyPage Page {get return (MyPage)Page; set;}'
Você não pode substituir a propriedade porque ela retorna um tipo diferente ... mas você pode redefini-la.
Você não precisa de covariância neste exemplo, pois é relativamente simples. Tudo o que você está fazendo é herdar o objeto base
Page
deMyPage
. Qualquer umControl
que você deseja retornar, emMyPage
vez de,Page
precisa redefinir aPage
propriedade doControl
fonte
Sim, ele oferece suporte à covariância, mas depende do que você está tentando alcançar exatamente.
Eu também costumo usar muito os genéricos para as coisas, o que significa que quando você faz algo como:
class X<T> { T doSomething() { } } class Y : X<Y> { Y doSomethingElse() { } } var Y y = new Y(); y = y.doSomething().doSomethingElse();
E não "perder" seus tipos.
fonte
Este é um recurso para o próximo C # 9.0 (.Net 5), do qual você pode baixar uma versão de amostra agora.
O código a seguir agora é compilado com sucesso (sem fornecer
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(); }
fonte
Eu não tentei, mas isso não funciona?
fonte
Sim. Existem várias maneiras de fazer isso, e esta é apenas uma opção:
Você pode fazer sua página implementar alguma interface personalizada que exponha um método chamado "GetContext" ou algo assim, e retorne suas informações específicas. Em seguida, seu controle pode simplesmente solicitar a página e o elenco:
var myContextPage = this.Page as IMyContextGetter; if(myContextPage != null) var myContext = myContextPage.GetContext();
Então você pode usar esse contexto como desejar.
fonte
Você pode acessar sua página a partir de qualquer controle subindo pela árvore pai. Isso é
myParent = this; while(myParent.parent != null) myParent = myParent.parent;
* Não compilou ou testou.
Ou obtenha a página principal no contexto atual (depende da sua versão).
Então o que eu gosto de fazer é o seguinte: Eu crio uma interface com as funções que desejo usar no controle (por exemplo, IHostingPage)
Então eu transmito a página pai 'IHostingPage host = (IHostingPage) Parent;' e estou pronto para chamar a função na página que preciso do meu controle.
fonte
Vou fazer desta forma:
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; } } }
fonte