2015-10-22 3 views
6

Ich suche diese Schnittstellen hinzuzufügen:Was ist mehrdeutig an diesem abgeleiteten Interface Getter?

public interface IIndexGettable<TKey, TValue> 
{ 
    TValue this[TKey key]{ get; } 
} 

public interface IIndexSettable<TKey, TValue> 
{ 
    TValue this[TKey key]{ set; } 
} 

public interface IIndexable<TKey, TValue> : IIndexGettable<TKey, TValue>, IIndexSettable<TKey, TValue> 
{ 
    // new TValue this[TKey key]{ get; set; } // as suggested by SO question 1791359 this will "fix" the issue. 
} 

Wenn ich, ich Fehler erhalte die folgenden Compiler dann versuchen, einen IIndexable ‚s Getter zu verwenden:

The call is ambiguous between the following methods or properties: 'IIndexGettalbe<TKey, TValue>.this[Tkey]' and 'IIndexSettable<TKey, TValue>.this[TKey]'

Hier ist der Code I lief:

public static class IIndexableExtensions 
{ 
    public static Nullable<TValue> AsNullableStruct<TKey, TValue>(this IIndexable<TKey, object> reader, TKey keyName) where TValue : struct 
    { 
     if (reader[keyName] == DBNull.Value) // error is on this line. 
     { 
      return null; 
     } 
     else 
     { 
      return (TValue)reader[keyName]; 
     } 
    } 
} 

Sollte es nicht eindeutig den Getter von der IIndexGettable erben a nd den Setter von der IIndexSetable?

Übrigens, ich versuche nicht, diesen Klang in Richtung Sprachdesign zu entzünden. Ich bin mir sicher, dass es einen guten Grund dafür gibt, mein Ziel ist es zu verstehen, was der Grund ist, um die Sprache besser zu verstehen.

+0

Off-Thema vielleicht, aber wie wäre es mit Methode statt Eigenschaft? – Ksv3n

+0

@ Ksv3n - mein Ziel ist es, Dinge wie Wörterbuch zu IIndexGettable usw. zu übersetzen. Sonst würde das funktionieren, danke. – user420667

+3

Nun, Sie können 'Dictionary ' nicht auf 'IIndexGetable ' anwenden, weil es diese Schnittstelle nicht implementiert (es hat zufälligerweise eine Methode mit der gleichen Signatur, die nicht die gleiche Sache ist). Also, wenn das dein Ziel ist, bellst du den falschen Baum an. Sie müssten eine _new_-Klasse erstellen, die "Dictionary_ _" erbt (oder einkapselt) und die neue (n) Schnittstelle (n) implementiert. –

Antwort

2

Versucht den untenstehenden Code, und es funktioniert gut. Was ist das Szenario Ihres Fehlers? Was ist nicht in diesem Code?

class Program 
    { 
     static void Main(string[] args) 
     { 
      Foo<int, string> foo = new Foo<int, string>(); 
      foo.dict.Add(1, "a"); 
      foo.dict.Add(2, "b"); 
      Console.WriteLine(((IIndexGettable<int, string>)foo)[1]); 
      ((IIndexSettable<int, string>)foo)[3] = "c"; 
      Console.WriteLine(foo[3]); 
      Console.ReadLine(); 
     } 
    } 

    public interface IIndexGettable<TKey, TValue> 
    { 
     TValue this[TKey key] { get; } 
    } 

    public interface IIndexSettable<TKey, TValue> 
    { 
     TValue this[TKey key] { set; } 
    } 

    public interface IIndexable<TKey, TValue> : IIndexGettable<TKey, TValue>, IIndexSettable<TKey, TValue> 
    { 

    } 

    public class Foo<TKey, TValue> : IIndexable<TKey, TValue> 
    { 
     public Dictionary<TKey, TValue> dict = new Dictionary<TKey, TValue>(); 
     public TValue this[TKey key] 
     { 
      get 
      { 
       return dict[key]; 
      } 

      set 
      { 
       dict[key] = value; 
      } 
     } 

    } 
+0

Ich aktualisierte das OP, um zu zeigen, wie ich es benutzte, also habe ich den Fehler bekommen.Es scheint keinen Fehler zu verursachen, bis Sie tatsächlich ein IIndexable-Objekt erstellen und versuchen, auf den indizierten Getter zuzugreifen. – user420667

+0

Hm ... Ich habe versucht, Ihren Code und meinen Code, aber für das Leben von mir kann nicht sehen, warum meins scheitern würde und Ihres nicht. – user420667

+0

Ich habe Ihre Extension-Methode zu meinem Code hinzugefügt, und es zeigt auch den Compiler-Fehler für mich. Und nicht nur, wenn Sie in Ihrer "if" -Klausel vom Leser lesen, sondern auch in der "else" -Klausel. – singsuyash

1

Um Ihre Frage zu beantworten „Was ist mit diesem abgeleiteten Schnittstelle Getter nicht eindeutig ist?“

Der Typ, für den die Eigenschaft (Parameterful property/accessor) definiert ist.

Es scheint, der Compiler würde den Typ zuerst überprüfen, wo die Eigenschaft & dann definiert ist, wenn es ein Getter oder ein Setter ist.

Hier haben wir eine Eigenschaft mit dem gleichen Namen & Signatur in 2 Schnittstellen definiert und das ist die Ambiguität. Es gibt Compiler keine Änderung, um zum nächsten Schritt zu gehen, überprüfen Sie auch, ob es ein Getter oder ein Setter ist. Hier

ist ein kurzes Experiment, das ich tat, um sein Verhalten auf Intellisence beobachten zu -

Ich habe ein einfacheren Aufbau -

public interface IIndexGettable 
{ 
    int Index { get; } 
} 

public interface IIndexSettable 
{ 
    int Index { set; } 
} 

public interface IIndexable : IIndexGettable, IIndexSettable 
{ 

} 

Und ich jetzt Intellisence verwenden, um die Schnittstelle zu implementieren IIndexable

Und hier ist das Problem

public class ConcreteIndexable : IIndexable 
{ 
    public int Index 
    { 
     get { throw new NotImplementedException(); } 
    } 

    int IIndexSettable.Index 
    { 
     set { throw new NotImplementedException(); } 
    } 
} 

Es implementiert int IIndexSettable.Index als Explizite Schnittstellenimplementierung wie es es getan hätte, wenn beide Schnittstellen die gleiche Funktion haben.

Jetzt hätte dieser automatisch generierte Code perfekten Sinn ergeben, wenn es zwei Funktionen in beiden Schnittstellen mit dem gleichen Namen & Unterschrift gegeben hätte. Aber wie wir sehen, behandelt es auch eine Eigenschaft mit nur get und eine Eigenschaft mit nur set, als ob sie genau gleich wären, und erstellt eine explizite Schnittstellenimplementierung. Und das ist der Punkt, den ich vermitteln wollte. Wenn Sie sich den automatisch generierten Code ansehen, passt er nicht zusammen - eine explizite Schnittstellenimplementierung mit set.

+0

Danke für die Bereitstellung eines einfacheren Beispiels. Interessant zu bemerken, dass Sie den Setter in Ihrem 'ConcreteIndexable' nicht veröffentlichen können. – user420667

+0

@ user420667 Sie meinen "öffentliche" Zugänglichkeit zu "int IIndexSetable.Index" hinzufügen? Es ist nicht möglich, es als öffentlich oder privat zu markieren, weil es eine explizite Schnittstellenimplementierung ist. Beim Generieren der Metadaten markiert der C# -Compiler sie als privat. Dort verhindern Sie, dass ein Code, der die Instanz der Klasse verwendet, sie einfach aufruft. Die Schnittstelleneigenschaft kann nur über eine Variable des Schnittstellentyps aufgerufen werden. – Kapoor

+0

@ user420667 - Nun wäre das sehr sinnvoll gewesen, wenn es in beiden Schnittstellen zwei Funktionen mit gleichem Namen und gleicher Signatur gäbe. Aber wie wir sehen, handhabt es auch eine Eigenschaft mit nur 'get' und eine Eigenschaft mit 'set', als ob sie genau gleich wären, und erstellt eine explizite Schnittstellenimplementierung. Und das ist der Punkt, den ich vermitteln wollte. Wenn Sie sich den automatisch generierten Code anschauen, passt er nicht zusammen - eine explizite Schnittstellenimplementierung mit 'set'. (Ich werde es auch meiner Antwort hinzufügen.) – Kapoor

0

Wie Sie in Ihrer Frage festgestellt haben, wird das Problem durch Hinzufügen des Schlüsselworts new behoben. Aber ein besserer Ansatz insgesamt wäre einfach zu haben:

public interface IReadOnlyIndexable<TKey, TValue> : IEnumerable<TKey, TValue> 
{ 
    TValue this[TKey key]{ get; } 
    int Count { get; } 
} 

public interface IIndexable<TKey, TValue> : IReadOnlyIndexable<TKey, TValue> 
{ 
    new TValue this[TKey key]{ get; set; } 
} 

Eine Sache ist, dass Sie selten (wenn überhaupt) wollen ohne Getter eine Eigenschaft Setter haben (ich habe es nie benutzt, es nie in BCL gesehen oder irgendeine bekannte Bibliothek). Eine Eigenschaft nur Setter zu haben würde höchstwahrscheinlich bedeuten, dass Sie etwas falsch machen.

Und das können Sie tun, was OOP Sie tun lassen soll: übergeben Sie eine Instanz von IIndexable<T, V> zu Methoden, die IReadOnlyIndexable<TKey, TValue> akzeptieren, und stellen Sie sicher, dass sie nicht in der Lage sein werden, es zu mutieren.

.NET BCL, ab .NET 4.5, hat einen anderen Ansatz; Es definiert IList<T>/IReadOnlyList<T> und IDictionary<T, V>/IReadOnlyDictionary<T, V>, aber änderbare Schnittstellen erben nicht von schreibgeschützten. Stattdessen implementieren BCL-Klassen beide Schnittstellen:

public class List<T> : IList<T>, IReadOnlyList<T> ... 

public class Dictionary<TKey, TValue> : IDictionary<TKey, TValue>, 
     IReadOnlyDictionary<TKey, TValue>, ... 

Ich mag die erste (geerbt) Ansatz besser, weil sie ein Verfahren Referenzierung eine Instanz durch seine veränderbare Schnittstelle ermöglicht die Instanz auf eine Methode zu übergeben eine unveränderliche Instanz benötigen (was Sie können mit BCL-Klassen nicht tun):

public void ReadStuff(IReadOnlyIndexable<T, K> instance) 
{ 
    // read from instance 
} 

public void ReadOrWrite(IIndexable<T, K> instance) 
{ 
    // write to instance 
    ReadStuff(instance); // and you can simply pass it as IReadOnlyIndexable 
} 

Btw, ich habe in this article vor so etwas wie dies vor ein paar Jahren gemacht, obwohl es sich um eine Schnittstelle für Listen/Sammlungen ist nur. Aber die Idee hinter dem Artikel war wahrscheinlich ähnlich wie deine: Ich wollte Sammlungen in einem leichten, schreibgeschützten Wrapper verpacken, den ich weitergeben konnte, ohne mich darum zu kümmern, ob ein Teil meines Codes ihn versehentlich mutieren würde. Und ich könnte coole LINQ-ähnliche Erweiterungsmethoden hinzufügen, mit denen ich Segmente meiner Sammlungen umhüllen und sie als unveränderlich weitergeben kann.

Verwandte Themen