Почему инициализаторы коллекции C # работают именно так? - PullRequest
57 голосов
/ 20 января 2009

Я смотрел на инициализаторы коллекции C # и обнаружил, что реализация очень прагматична, но также очень непохожа на что-либо еще в C #

Я могу создать такой код:

using System;
using System.Collections;

class Program
{
    static void Main()
    {
        Test test = new Test { 1, 2, 3 };
    }
}

class Test : IEnumerable
{
    public IEnumerator GetEnumerator()
    {
        throw new NotImplementedException();
    }

    public void Add(int i) { }
}

Поскольку я выполнил минимальные требования к компилятору (реализовано IEnumerable и public void Add), это работает, но, очевидно, не имеет значения.

Мне было интересно, что помешало команде C # создать более строгий набор требований? Другими словами, почему для компиляции этого синтаксиса компилятору не требуется, чтобы тип реализовывал ICollection? Это кажется больше в духе других возможностей C #.

Ответы [ 3 ]

90 голосов
/ 20 января 2009

Ваши наблюдения точны - на самом деле они отражают наблюдения, сделанные Мэдсом Торгерсеном, премьер-министром по языку Microsoft C #.

Мэдс сделал пост в октябре 2006 года на эту тему под названием Что такое коллекция? , в котором он написал:

Допустим, мы взорвали это в первом версия фреймворка с System.Collections.ICollection, которая рядом с бесполезным. Но мы исправили это довольно хорошо, когда появились дженерики в .NET Framework 2.0: System.Collections.Generic.ICollection позволяет добавлять и удалять элементы, перечисли их, посчитай и проверь для членства.

Очевидно, что с этого момента все применять ICollection каждый раз они делают коллекцию, верно? Не так. Вот как мы использовали LINQ, чтобы узнать о том, что коллекции на самом деле, и как это заставило нас изменить наш язык дизайн в C # 3.0.

Оказывается, что в платформе есть только 14 реализаций ICollection<T>, но 189 классов, которые реализуют IEnumerable и имеют открытый метод Add().

У этого подхода есть скрытое преимущество - если бы они основывали его на интерфейсе ICollection<T>, был бы точно один поддерживаемый метод Add().

Напротив, подход, который они использовали, означает, что инициализаторы для коллекции просто формируют наборы аргументов для методов Add().

Для иллюстрации давайте немного расширим ваш код:

class Test : IEnumerable
{
    public IEnumerator GetEnumerator()
    {
        throw new NotImplementedException();
    }

    public void Add(int i) { }

    public void Add(int i, string s) { }
}

Теперь вы можете написать это:

class Program
{
    static void Main()
    {
        Test test 
            = new Test 
            {
                1, 
                { 2, "two" },
                3 
            };
    }
}
9 голосов
/ 20 января 2009

Я тоже думал об этом, и ответ, который меня больше всего удовлетворяет, состоит в том, что ICollection имеет много методов, отличных от Add, таких как: Clear, Contains, CopyTo и Remove. Удаление элементов или очистка не имеют ничего общего с возможностью поддерживать синтаксис инициализатора объекта, все, что вам нужно, это Add ().

Если бы фреймворк был спроектирован достаточно детально, и был интерфейс ICollectionAdd, то у него был бы "идеальный" дизайн. Но я, честно говоря, не думаю, что это принесло бы большую пользу, имея один метод на интерфейс. IEnumerable + Add выглядит хакерским подходом, но если подумать, это лучшая альтернатива.

РЕДАКТИРОВАТЬ: Это не единственный раз, когда C # подошел к проблеме с этим типом решения. Начиная с .NET 1.1, foreach использует утечную типизацию для перечисления коллекции, все, что нужно реализовать вашему классу - это GetEnumerator, MoveNext и Current. У Кирилла Осенкова есть пост , который также задает ваш вопрос.

3 голосов
/ 28 сентября 2012

(я знаю, что опоздал на 3 года, но я не был удовлетворен существующими ответами.)

почему, для компиляции этого синтаксиса, компилятор не требует, чтобы тип реализовал ICollection?

Я переверну ваш вопрос: какая польза от этого, если у компилятора есть требования, которые на самом деле не нужны?

Классы, не являющиеся ICollection, также могут использовать синтаксис инициализатора коллекции. Рассмотрим классы, которые позволяют добавлять в них данные, не предоставляя доступ к ранее добавленным данным.

Лично мне нравится использовать синтаксис new Data { { ..., ... }, ... } для добавления легкого DSL-подобного вида к коду моих модульных тестов.

На самом деле, я бы предпочел ослабить требование, чтобы я мог использовать красивый синтаксис, даже не потрудившись реализовать IEnumerable. Инициализаторы коллекций - это чистый синтаксический сахар для Add (), они не должны требовать ничего другого.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...