2009-01-30 1 views
38

Warum können Sie in .NET keinen generischen Indexer erstellen?Warum ist es nicht möglich, generische Indexer in .NET zu definieren?

der folgende Code führt einen Compiler-Fehler:

public T this<T>[string key] 
    { 
     get { /* Return generic type T. */ } 
    } 

das Sie bedeutet das eine generische Indexer für eine generische Element Sammlung nicht schaffen können?

+0

Welche Verwendungssyntax möchten Sie? Obj [i] sieht aus wie es mit dem Vergleich obj mit T in Konflikt wäre, obj [i] ist besser, aber ich finde es schwer zu lesen. Meine Präferenz ist obj [ ich], aber nicht viel. –

+0

Ich verstehe nicht, warum wir nicht nur eine generische Methode verwenden möchten. Es ist sauberer, IMO, da das Hinzufügen von Generics zu Indizes zu schwer lesbarem Code führen würde. – William

Antwort

1

Das einzige, was ich daran denken kann, kann verwendet werden, ist etwas in dieser Richtung:

var settings = ConfigurationSection.AppSettings; 
var connectionString = settings<string>["connectionString"]; 
var timeout = settings<int>["timeout"]; 

Aber das ist eigentlich Sie nicht alles kaufen. Sie haben gerade rund Pars (wie in (int) Einstellungen [ „timeout“]) mit spitzen Klammern ersetzt, erhielten aber keine zusätzliche Typsicherheit, wie Sie frei

var timeout = settings<int>["connectionString"]; 

tun können, wenn Sie etwas, das stark ist, aber nicht statisch getippt, möchten Sie vielleicht bis C# 4.0 mit seinem dynamischen Schlüsselwort warten.

+5

Nun, eigentlich die Einstellungen Syntax ist vorzuziehen. C-Style-Casts zwingen Sie oft dazu, zusätzliche Klammern einzufügen. –

+0

@John Ich stimme zu. Ich denke, das ist schöner, als Methodenaccessoren wie Filed <> und SetFiled <> in der DataRow-Klasse zu erstellen. –

+0

Es würde Typ Sicherheit hinzufügen, wenn das Lesen 'Einstellungen [" foo "]' würde das letzte Ding in 'Einstellungen [" foo "]' gespeichert, ohne Rücksicht darauf, ob etwas in 'Einstellungen geschrieben wurde [" foo " ] 'vorher oder nachher. Das größte, was über solch ein Design zu beachten ist, ist, dass nach den Einstellungen ["foo"] = someSiameseCat, ein Versuch, die Einstellungen ["foo"] zu lesen, diese Katze nicht sehen würde, während nach nicht-generischen 'Einstellungen [ "foo"] = someSiameseCat; "myAnimal = (Animal) Einstellungen [" SiameseCat "] würde es sehen. – supercat

9

Sie können; lassen Sie einfach den <T> Teil von Ihrer Deklaration fallen und es wird gut funktionieren. das heißt

public T this[string key] 
{ 
    get { /* Return generic type T. */ } 
} 

(Angenommen, Ihre Klasse mit einem Typ-Parameter namens T generisch).

+5

Das funktioniert nicht, wenn Sie auf Klassenebene definieren. –

+3

@Igor - deshalb habe ich gesagt "Angenommen, Ihre Klasse ist generisch mit einem Typparameter namens T". Die Frage wies nicht darauf hin, dass sich der Typ-Parameter vom Container unterscheiden sollte (und dies wäre in jedem Fall ein sehr ungewöhnliches Design!). –

+2

@Greg ... wie ungewöhnlich kann es sein? Nehmen Sie als Beispiel die allgemeine Methode Field <> in DataRow. Was sie getan haben, ist das Auspacken durch eine generische Methode, da dies durch einen generischen Indexer unmöglich ist. –

15

Ich weiß nicht warum, aber Indexer sind nur syntaktischer Zucker. Schreiben Sie stattdessen eine generische Methode und Sie erhalten die gleiche Funktionalität. Beispiel:

public T GetItem<T>(string key) 
    { 
     /* Return generic type T. */ 
    } 
17

Eigenschaften können in C# 2.0/3.0 nicht generisch sein, daher können Sie keinen generischen Indexer verwenden.

+2

Haben Sie einen Link zu irgendetwas an dieser Front? Ich kann es im Dokument "Neue Funktionen" nicht sehen. –

+0

Jon du bist ganz richtig, ich werde das zurückziehen. – Kev

+2

mmmm ... Ich glaube, ich habe den gesuchten Link gefunden. http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=352188 Was es mir unglaublich scheint, ist, dass Microsoft sagt, dass es kein nützliches Feature ist, aber bereits einige Workaround-Extender implementiert haben (dh DataRowExtender) –

23

Hier ist ein Platz, wo dies nützlich wäre. Angenommen, Sie haben eine stark typisierte OptionKey<T> zur Deklaration von Optionen.

public static class DefaultOptions 
{ 
    public static OptionKey<bool> SomeBooleanOption { get; } 
    public static OptionKey<int> SomeIntegerOption { get; } 
} 

Wo Optionen durch die IOptions Schnittstelle ausgesetzt sind:

public interface IOptions 
{ 
    /* since options have a default value that can be returned if nothing's 
    * been set for the key, it'd be nice to use the property instead of the 
    * pair of methods. 
    */ 
    T this<T>[OptionKey<T> key] 
    { 
     get; 
     set; 
    } 

    T GetOptionValue<T>(OptionKey<T> key); 
    void SetOptionValue<T>(OptionKey<T> key, T value); 
} 

-Code dann das generische Indexer als eine nette stark typisierte Optionen Speicher verwenden:

void Foo() 
{ 
    IOptions o = ...; 
    o[DefaultOptions.SomeBooleanOption] = true; 
    int integerValue = o[DefaultOptions.SomeIntegerOption]; 
} 
2

Ich mag die Fähigkeit, einen Indexer zu haben, ohne einen direkten Verweis auf den "indexierten" Gegenstand auszugeben. Ich schrieb eine einfache „Rückruf“ unter Indexer Klasse ...

R = die zurückTyp aus dem Indexer P = der übergebene Typ in den Indexer

das All Indexer wirklich tut, ist die Vorgänge passieren zu der Deployer und ermöglichen ihnen zu verwalten, was tatsächlich passiert und wird zurückgegeben.

public class GeneralIndexer<R,P> 
    { 
     // Delegates 
     public delegate R gen_get(P parm); 
     public delegate void gen_set(P parm, R value); 
     public delegate P[] key_get(); 

     // Events 
     public event gen_get GetEvent; 
     public event gen_set SetEvent; 
     public event key_get KeyRequest; 

     public R this[P parm] 
     { 
      get { return GetEvent.Invoke(parm); } 
      set { SetEvent.Invoke(parm, value); } 
     } 

     public P[] Keys 
     { 
      get 
      { 
       return KeyRequest.Invoke(); 
      } 
     } 

    } 

Um es in einem Programm oder Klasse zu verwenden:

private GeneralIndexer<TimeSpan, string> TimeIndex = new GeneralIndexer<TimeSpan,string>(); 

{ 
      TimeIndex.GetEvent += new GeneralIndexer<TimeSpan, string>.gen_get(TimeIndex_GetEvent); 
      TimeIndex.SetEvent += new GeneralIndexer<TimeSpan, string>.gen_set(TimeIndex_SetEvent); 
      TimeIndex.KeyRequest += new GeneralIndexer<TimeSpan, string>.key_get(TimeIndex_KeyRequest); 

} 

funktioniert wie ein Champion vor allem, wenn Sie den Zugriff auf Ihre Liste überwachen möchten oder besondere Operationen tun, wenn etwas zugegriffen wird.