2009-05-07 12 views
4
interface IFolderOrItem<TFolderOrItem> where TFolderOrItem : FolderOrItem {} 

abstract class FolderOrItem {} 

class Folder : FolderOrItem {} 

abstract class Item : FolderOrItem {} 

class Document : Item {} 

Jetzt versuche ich, etw wie dies zu tun:Warum ist dieser Cast nicht möglich?

class Something 
{ 
    IFolderItemOrItem<Item> SelectedItem { get; set; } 
    void SomeMagicMethod() 
    { 
     this.SelectedItem = (IFolderOrItem<Item>)GetMagicDocument(); 
     // bad bad bad ... ?? 
    } 
    IFolderOrItem<Document> GetMagicDocument() 
    { 
     return someMagicDocument; // which is of type IFolderOrItem<Document> 
    } 
} 

ist es eine Möglichkeit, diese Funktion zu erhalten?

+0

Was ist die Fehlermeldung, die es wirft? Zeit kompilieren? –

+0

ohne einen Cast = Kompilierzeit; mit Cast = Laufzeit –

+0

(Notiz per Kommentar zu meiner Antwort, habe ich einige Notizen auf einer nicht-generischen Basis-Schnittstelle, die es zumindest funktioniert) –

Antwort

13

Wenn ich es richtig gelesen ... dann ist das Problem, dass nur weil Foo : Bar, dass nicht bedeutet, dass ISomething<Foo> : ISomething<Bar> ...

In einigen Fällen Varianz in C# 4.0 eine Option sein kann. Alternativ gibt es manchmal Dinge, die Sie mit generischen Methoden tun können (nicht sicher, dass es hier hilft).


Die nächstgelegene Sie tun in kann C# 3.0 (und darunter) ist wahrscheinlich ein nicht-generische Basis Schnittstelle:

interface IFolderOrItem {} 
interface IFolderOrItem<TFolderOrItem> : IFolderOrItem 
    where TFolderOrItem : FolderOrItem { } 

häufig, die Basis-Schnittstelle würde zum Beispiel ein Type ItemType {get;} zu geben Sie den tatsächlich betrachteten Typ an. Dann Nutzung:

IFolderOrItem SelectedItem { get; set; } 
... 
public void SomeMagicMethod() 
{ 
    this.SelectedItem = GetMagicDocument(); // no cast needed 
    // not **so** bad 
} 

Von der Spezifikation bezieht sich dies auf §25.5.6 (ECMA 334 v4):

25.5.6 Konvertierungen

Konstruiert Typen folgen der gleichen Umwandlung Regeln (§13) wie nicht generische Typen. Bei der Anwendung dieser Regeln müssen die Basisklassen und die Schnittstellen der konstruierten Typen wie in §25.5.3 beschrieben festgelegt werden.

Es gibt keine speziellen Umwandlungen zwischen konstruierten Referenztypen außer die in §13 beschrieben. Insbesondere im Gegensatz zu Array-Typen, konstruiert Referenztypen erlauben nicht Co-Variante Umbauten (§19.5). Dies bedeutet, dass ein Typ List<B> hat keine Umwandlung (entweder implizit oder explizit ) zu List<A> selbst wenn B ist von A abgeleitet. Ebenso existiert keine Umwandlung von List<B> zu List<object>.

[Anmerkung: Die Begründung für ist dies einfach: Wenn eine Umstellung auf List<A> erlaubt ist, dann offenbar, eine Werte vom Typ A in die Liste zu speichern. Dies würde jedoch die invariant brechen, dass jedes Objekt in einer Liste vom Typ List<B> ist immer ein Wert vom Typ B oder auch unerwartete Ausfälle auftreten können, wenn sie in Collection-Klassen zuweisen. Endnote]

Das Gleiche gilt für Schnittstellen. Dies ändert ein Bit in C# 4.0, aber nur in einigen Fällen.

+0

Kannst du mir einen Hinweis geben, damit das funktioniert? –

+1

Tricky ... es * ist nicht * ein IFolderOrItem - wenn Sie es also nicht wickeln, wird es nicht funktionieren. Je nachdem, was die Mitglieder in der Schnittstelle sind, könnte es in 4.0 funktionieren (mit einem zusätzlichen Modifikator). Anders als das ... vielleicht deklarieren Sie eine nicht-generische Schnittstelle ... –

+0

+1: Ich wusste, dass Sie etwas finden würden :) –

6

Soweit der Compiler betroffen ist, IFolderOrItem<Document> & IFolderOrItem<Item> sind zwei völlig verschiedene Arten.

Document kann Item, erben aber IFolderOrItem<Document> erbt nicht IFolderOrItem<Item>

ich auf Marc oder Jon verlasse mich # spec posten Links zu den relevanten Teilen des C.

+0

lol - aber ich Glauben Sie, dass dies eines der Dinge ist, die nicht in der Spezifikation C# 3.0 sind, einfach weil es keine Überlegung ist. Es sollte aber etwas in der 4.0-Spezifikation geben. –

+0

Sag eine Lüge ... Aktualisierung; -p –

3

Das Problem ist, dass eine Umwandlung nicht an den generischen Argumenten, sondern an der Klasse als Ganzes funktioniert. Das Dokument erbt von Item, true, aber IFolderOrItem < Document> erbt nicht von IFolderOrItem < Item>, noch ist es in irgendeiner Weise damit verbunden.

2

Ein Beispiel zu verstehen, warum es so funktioniert:

Angenommen IFolderOrItem stellt eine Methode zum Beispiel void add (T-Element).

Ihre Implementierung für IFolderOrItem wird annehmen, dass der Parameter ein Dokument ist.

Aber von Ihnen caste Ihr IFolderOrItem als IFolderItemOrItem, dann könnte jemand die Methode Create (T) aufrufen, wo T ein Item sein soll.

Die Umwandlung von Element zu Dokument ist ungültig, da ein Element kein Dokument ist.

Die einzige Möglichkeit, dies zu tun, ist eine nicht-generische Version der Schnittstelle zu erstellen, die Objekte als Parameter erlaubt, die den Typ des Objekts in Ihren Implementierungen überprüfen.

Verwandte Themen