Czy C# wspiera kowariancję typu return?

Pracuję z. NET framework i naprawdę chcę być w stanie zrobić niestandardowy typ strony, że wszystkie moja strona używa. Problem pojawia się, gdy próbuję uzyskać dostęp do strony z kontrolki. Chcę mieć możliwość powrotu określonego typu strony zamiast domyślnej. Jest na to jakiś sposób?

public class MyPage : Page
{
    // My own logic
}

public class MyControl : Control
{
    public MyPage Page { get; set; }
}
Author: Kyle Sletten, 2011-04-19

9 answers

Wygląda na to, że to, czego chcesz, to KOWARIANCJA typu return. C# nie obsługuje kowariancji typu powrotu.

KOWARIANCJA typu zwrotnego polega na tym, że nadpisujesz metodę klasy bazowej, która zwraca mniej specyficzny typ, z tą, która zwraca bardziej specyficzny typ:

abstract class Enclosure
{
    public abstract Animal Contents();
}
class Aquarium : Enclosure
{
    public override Fish Contents() { ... }
}
Jest to bezpieczne, ponieważ konsumenci treści za pośrednictwem obudowy oczekują zwierzęcia, a Akwarium obiecuje nie tylko spełnić ten wymóg, ale ponadto złożyć bardziej rygorystyczną obietnicę: że zwierzę jest zawsze ryba.

Ten rodzaj kowariancji nie jest obsługiwany w C# i jest mało prawdopodobne, aby kiedykolwiek był obsługiwany. Nie jest obsługiwany przez CLR. (Jest on wspierany przez C++, oraz przez implementację C++ / CLI na CLR; robi to poprzez generowanie magicznych metod pomocniczych, takich jak sugeruję poniżej.)

(Niektóre języki również obsługują formalny parametr contravariance -- że można zastąpić metodę, która zabiera rybę, metodą, która zabiera zwierzę. Ponownie, umowa została spełniona; podstawa Klasa wymaga, aby obchodzić się z każdą rybą, a klasa pochodna obiecuje nie tylko obchodzić się z rybami, ale z każdym zwierzęciem. Podobnie, C# i CLR nie obsługują formalnych parametrów typu contravariance.)

Sposób obejścia tego ograniczenia polega na zrobieniu czegoś w stylu:

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

Teraz masz zarówno korzyści z nadpisania metody wirtualnej, jak i coraz silniejsze pisanie, gdy używasz czegoś typu Akwarium w czasie kompilacji.

 132
Author: Eric Lippert,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-31 22:46:02

Umieszczenie tego w obiekcie MyControl zadziałałoby:

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

Nie można nadpisać właściwości, ponieważ zwraca inny typ... ale możesz to przedefiniować.

Nie potrzebujesz kowariancji w tym przykładzie, ponieważ jest ona stosunkowo prosta. Wszystko, co robisz, to dziedziczenie obiektu bazowego Page z MyPage. Każda Control, którą chcesz zwrócić MyPage zamiast Page, musi przedefiniować Page Właściwość Control

 2
Author: Zhais,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-04-18 21:39:56

Ta seria postów na blogu omawia szczegółowo problem kowariancji typu powrotu, nawet mówiąc o tym, jak to zrobić z 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

Przepraszam za zamieszczanie linków, ale jest to dość szczegółowe a cytowanie fragmentów tutaj nie byłoby aż tak pomocne. Pokazuje, jak można to osiągnąć za pomocą kodu IL.

 2
Author: hatchet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-26 17:47:22

Tak, wspiera kowariancję, ale zależy od tego, co dokładnie próbujesz osiągnąć.

Ja też często używam leków generycznych, co oznacza, że gdy robisz coś w stylu:

class X<T> {
    T doSomething() {
    }

}

class Y : X<Y> {
    Y doSomethingElse() {
    }
}

var Y y = new Y();
y = y.doSomething().doSomethingElse();
I nie "tracić" swoich typów.
 1
Author: Cade Roux,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-04-18 21:22:18

Z interfejsami obejrzałem to jawnie implementując Interfejs:

public interface IFoo {
  IBar Bar { get; }
}
public class Foo : IFoo {
  Bar Bar { get; set; }
  IBar IFoo.Bar => Bar;
}
 1
Author: ChetPrickles,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-29 13:41:45

Nie próbowałem, ale czy to nie działa?

YourPageType myPage = (YourPageType)yourControl.Page;
 0
Author: AGoodDisplayName,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-04-18 21:21:48

Tak. Istnieje wiele sposobów, aby to zrobić, a to jest tylko jedna opcja:

Możesz sprawić, że Twoja strona zaimplementuje jakiś niestandardowy interfejs, który wyświetla metodę o nazwie "GetContext" lub coś takiego i zwraca konkretne informacje. Wtedy twoja kontrola może po prostu zażądać strony i rzucić:

var myContextPage = this.Page as IMyContextGetter;

if(myContextPage != null)
   var myContext = myContextPage.GetContext();

Wtedy możesz użyć tego kontekstu, jak chcesz.

 0
Author: Tejs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-04-18 21:21:51

Możesz uzyskać dostęp do swojej strony z dowolnego sterowania, wchodząc w drzewo nadrzędne. To jest

myParent = this;

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

* nie kompilował ani nie testował.

Lub uzyskać Stronę nadrzędną w bieżącym kontekście(zależy od wersji).


Wtedy lubię robić to: tworzę interfejs z funkcjami, których chcę używać w sterowaniu (na przykład IHostingPage)

Następnie oddaję Stronę nadrzędną 'IHostingPage host = (IHostingPage) Parent;' i jestem ustawiony na wywołanie funkcji na stronie I potrzebuję od mojej kontroli.

 0
Author: Hogan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-04-18 21:26:57

Zrobię to w ten sposób:

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; } }
}
 0
Author: Indomitable,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-18 10:06:41