70 votes

Pourquoi ne pas ICollection générique de mettre en œuvre IReadOnlyCollection dans .NET 4.5?

Dans .NET 4.5 / C# 5, IReadOnlyCollection<T> est déclarée avec un Count de la propriété:

public interface IReadOnlyCollection<out T> : IEnumerable<T>, IEnumerable
{
    int Count { get; }
}

Je me demande, ne serait-il pas logique pour l' ICollection<T> pour mettre en œuvre l' IReadOnlyCollection<T> interface:

public interface ICollection<T> : IEnumerable<T>, IEnumerable, *IReadOnlyCollection<T>*

Cela aurait signifié que les classes qui implémentent ICollection<T> avons mis en place automatiquement IReadOnlyCollection<T>. Cela semble raisonnable pour moi.

L' ICollection<T> d'abstraction peut être considéré comme une extension de l' IReadOnlyCollection<T> d'abstraction. Notez que List<T>, par exemple, met en œuvre les deux ICollection<T> et IReadOnlyCollection<T>.

Cependant, il n'a pas été conçu de cette façon.

Ce qui me manque ici? Pourquoi l'actuelle mise en œuvre ont été choisis à la place?


Mise à JOUR

Je suis à la recherche d'une réponse qui utilise la conception Orientée Objet raisonnement pour expliquer pourquoi:

  • Une classe concrète comme l' List<T> mise en œuvre à la fois IReadOnlyCollection<T> et ICollection<T>

une meilleure conception que:

  • ICollection<T> œuvre IReadOnlyCollection<T> directement

Veuillez également noter que c'est essentiellement la même question:

  1. Pourquoi ne peut - IList<T> œuvre IReadOnlyList<T>?
  2. Pourquoi ne peut - IDictionary<T> œuvre IReadOnlyDictionary<T>?

45voto

Jon Points 194296

Il y a probablement plusieurs raisons. Voici deux sur le dessus:

  1. Énorme en arrière des problèmes de compatibilité

    Comment écririez-vous la définition de l' ICollection<T>? Cela semble naturel:

    interface ICollection<T> : IReadOnlyCollection<T>
    {
        int Count { get; }
    }
    

    Mais il a un problème, parce qu' IReadOnlyCollection<T> déclare en outre un Count de la propriété (le compilateur émet un avertissement ici). En dehors de l'avertissement, laissant comme ça (ce qui est équivalent à l'écriture new int Count) permet aux développeurs d'avoir différentes implémentations pour les deux Count propriétés en mettant en œuvre au moins un explicitement. Ce pourrait être "amusant" si les deux implémentations décidé de renvoyer des valeurs différentes. Permettre aux gens de tirer eux-mêmes dans le pied est plutôt pas C#'s style.

    OK, donc ce sujet:

    interface ICollection<T> : IReadOnlyCollection<T>
    {
        // Count is "inherited" from IReadOnlyCollection<T>
    }
    

    Eh bien, cela casse tout le code existant qui a décidé de mettre en oeuvre Count explicitement:

    class UnluckyClass : ICollection<Foo>
    {
         int ICollection<Foo>.Count { ... } // compiler error!
    }
    

    Par conséquent, il me semble qu'il n'y a pas de bonne solution à ce problème: soit vous vous cassez le code existant, ou vous forcer un risque d'erreur de mise en œuvre sur tout le monde. Donc, le seul coup gagnant n'est pas de jouer.

  2. La sémantique d'exécution vérifications de type

    Il serait inutile d'écrire du code comme

    if (collection is IReadOnlyCollection<Foo>)
    

    (ou l'équivalent avec la réflexion), à moins d' IReadOnlyCollection<T> est "opt-in": si ICollection<T> ont un sur-ensemble de l' IReadOnlyCollection<Foo> alors à peu près chaque classe de collection sous le soleil allait se passer ce test, il n'est pas utile dans la pratique.

19voto

Notoriousxl Points 625

Jon est ici http://stackoverflow.com/a/12622784/395144 , vous devez marquer sa réponse à la réponse:

int ICollection<Foo>.Count { ... } // compiler error!

Depuis les interfaces peuvent explicite d'implémentations, l'extraction d'interfaces de base n'est pas compatible (avec les classes de base, vous n'avez pas ce problème).

C'est pourquoi...

Collection<T> : IReadOnlyCollection<T>
List<T> : IReadOnlyList<T>
Dictionary<TKey, TValue> : IReadOnlyDictionary<TKey, TValue>

... mais pas leurs interfaces.

À mon humble avis, ils ont fait une erreur de conception au départ, tout à fait insolubles maintenant (sans casser des choses).

EDIT: cacher ne vous aide pas, les vieux (explicite) des implémentations ne sera pas toujours construire (sans modification du code):

interface INew<out T> { T Get(); }

interface IOld<T> : INew<T>
{
    void Set(T value);
    new T Get();
}

class Old<T> : IOld<T>
{
    T IOld<T>.Get() { return default(T); }
    void IOld<T>.Set(T value) { }
}

'Échantillon.Vieux " n'implémente pas l'interface membre de l'Échantillon.INew.Get()'

-2voto

herzmeister Points 7077

Il serait sémantiquement mal, parce que, évidemment, pas tous ICollection est en lecture seule.

Cela dit, ils pourraient avoir appelé l'interface IReadableCollection, tandis que la mise en œuvre pourrait être appelé ReadOnlyCollection.

Cependant, ils n'ont pas à aller dans cette voie. Pourquoi? J'ai vu un BCL membre de l'équipe d'écriture qu'ils ne voulaient pas que les collections de l'API de devenir trop alambiqué. (Même si c'est déjà le cas, franchement.)

-3voto

n8wrl Points 12485

Quand j'ai lu votre question, j'ai pensé: "Hé, il est à droite! Que ferait le plus de sens." Mais le point d'une interface est de décrire un contrat - Une description de comportement. Si votre classe implémente IReadOnlyCollection, il doit être en lecture seule. Collection<T> ne l'est pas. Donc, pour l' Collection<T> d'implémenter une interface implicite en lecture seule peut conduire à des jolies cochonnes des problèmes. Les consommateurs de l' IReadOnlyCollection vont faire certaines hypothèses à propos de cette collection sous-jacente comme l'itération, c'est sûr, car on ne peut la modifier, etc. etc. Si elle était structurée comme vous le suggérez, qui pourrait ne pas être vrai!

-4voto

jeuton Points 349

La conception Orientée Objet raisonnement résulterait de l' "Est"Une relation entre une classe et d'interface que la classe implémente.

Dans .NET 4.5/ c# 5.0 List<T> est à la fois un ICollection<T> et IReadOnlyCollection<T>. Vous pouvez instancier un List<T> que soit le type de fonction de comment vous voulez que votre collection à être utilisé.

Tout en ayant ICollection<T> œuvre IReadOnlyCollection<T> serait vous donner la possibilité d'avoir des List<T> être ICollection<T> ou IReadOnlyCollection<T>, cela pourrait être dire qu' ICollection<T> "Est Un" IReadOnlyCollection<T> qui ne l'est pas.

Mise à jour

Si chaque classe héritée de ICollection<T> mis en oeuvre IReadOnlyCollection<T> alors je dirais qu'il est plus logique d'avoir des ICollection<T> implémenter l'interface. Puisque ce n'est pas le cas, pensez à quoi il ressemblerait s' ICollection<T> étaient de mettre en œuvre IReadOnlyCollection<T>:

public interface IReadOnlyCollection<T> : IEnumerable<T>, IEnumerable{}

Si vous regardez la signature d'
,, on dirait que c' EST-UNE collection en lecture seule. Attendez, voyons IReadOnlyCollection et ahh...il implémente public interface ICollection<T> : IReadOnlyCollection<T> {} et ICollection<T> donc il ne doit pas nécessairement être une collection en lecture seule. Sur le plan fonctionnel, les implémentations proposées par la question de départ sont les mêmes, mais sémantiquement je crois qu'il est plus correct de pousser une interface de mise en œuvre aussi haut qu'il peut aller.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X