2016-10-11 11 views
1

Могу ли я использовать инициализатор коллекции для моего класса, который имеет метод Add, который принимает общий параметр?Инициализатор коллекции в параметре Add method generic?

Мой класс выглядит следующим образом:

public class FooCollection : IEnumerable<KeyValuePair<string, Type>> 
{ 
    private readonly IDictionary<string, Type> _directory = new Dictionary<string, Type>(); 

    public void Add<T>(string name) 
    { 
     _directory.Add(name, typeof(T)); 
    } 

    public IEnumerator<KeyValuePair<string, Type>> GetEnumerator() 
    { 
     return _directory.GetEnumerator(); 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return _directory.GetEnumerator(); 
    } 
} 

я могу сделать:

var collection = new FooCollection(); 
collection.Add<Foo>("Foo"); 
collection.Add<Bar>("Bar"); 
collection.Add<Baz>("Baz"); 

Но я хотел бы использовать коллекцию инициализатор. Я могу это сделать? Как?

+0

Это не имеет смысла. Если вы назовите его так же, как класс, когда вы когда-нибудь будете иметь тот же класс с другим именем класса? Вы хотите создать экземпляр всего словаря сразу? Тогда почему у вас есть метод добавления? Просто сделайте свой словарь каталогов общедоступным, а затем инициализатор коллекции, как и в любом другом месте. – Dispersia

+0

Поскольку словарь может принимать только экземпляры объектов. Вы не можете вставить тип в словарь, не делая '.Add (typeof (Foo),« Foo »)' и использование 'typeof' является уродливым, также вы не можете ограничить тип определенным типом, например' Foo' или любой класс, который происходит от 'Foo'. – Fred

ответ

0

Я не думаю, что это возможно с помощью общего метода Add. Поскольку между аргументом generic type и фактическим аргументом нет никакой связи, он не может определить общий тип через инициализатор коллекции.

идея может быть, чтобы добавить перегрузки ищет что-то вроде этого:

public void Add(string name, Type t) 
{ 
    _directory.Add(name, t); 
} 

И затем использовать коллекции инициализатору:

var collection = new FooCollection() 
{ 
    { "Foo", typeof(Foo) }, 
    { "Bar", typeof(Bar) }, 
    { "Baz", typeof(Baz) }, 
}; 

К сожалению, как вы правильно отмечаете, это исключает возможность добавления ограничений общего типа. Если вы фактически не предоставляете аргумент типа общего типа, который можно использовать для вывода типа, я не думаю, что это возможно.

+0

Да, я надеялся избежать этого, поэтому я сделал класс в первую очередь, иначе я просто использовал бы «Словарь» напрямую. Причина, по которой я хотел этого избежать, заключается в том, что тогда у пользователя есть 'typeof', что довольно уродливо, и потому что тогда он принимает любой тип, и я не могу ограничить тип' Foo' или любой класс, который происходит от 'Foo'. – Fred

+0

Я вижу вашу точку зрения. К сожалению, я не знаю решения, которое позволило бы это ограничение во время компиляции. Как уже упоминалось, вам нужно каким-то образом указать тип, и стандартный инициализатор коллекции не позволяет этого. – MAV

1

Вы можете попробовать это:

public static class StringToTypeMapExtensions 
    { 
     public static void Add<T>(this StringToTypeMap map, T prototype) 
      where T : SomeBase 
     { 
      map.Add(typeof(T).Name, typeof(T)); 
     } 
    } 

    public class StringToTypeMap : Dictionary<string, Type> 
    { 
    } 

    public class SomeBase { } 
    public class Foo : SomeBase { } 
    public class Bar : SomeBase { } 
    public class Baz : Bar { } 
    public class Alien { } 

, который позволит вам написать:

class Program 
    { 
     static void Main(string[] args) 
     { 
      var map = 
       new StringToTypeMap 
       { 
        default(Foo), 
        default(Bar), 
        default(Baz) 
       }; 
      foreach (var key in map.Keys) 
      { 
       Console.WriteLine("{0} : {1}", key, (object)map[key] ?? "(null)"); 
      } 
      Console.ReadKey(); 
     } 
    } 

(Таким образом, избегая, чтобы заставить вас в реализующего ICollection < Type> только ради его Add (Тип))

Или это, также, будет скомпилироваться и работать как ожидалось:

  var map = 
       new StringToTypeMap 
       { 
        { string.Empty, null }, 
        { "Integer", typeof(int) }, 
        { nameof(Decimal), typeof(decimal) }, 

        default(Foo), 
        default(Bar), 
        default(Baz) 
       }; 
      foreach (var key in map.Keys) 
      { 
       // etc 
      } 

Но это не компилируется (из-за Чужого):

  var map = 
       new StringToTypeMap 
       { 
        default(Foo), 
        default(Bar), 
        default(Baz), 
        default(Alien) 
       }; 

(Это работает, потому что коллекция Инициализаторы чтить «Добавить» методы расширения, когда те находятся в области видимости)

См: https://msdn.microsoft.com/en-us/library/bb384062.aspx

(цитирую)

«Коллекция инициализаторы позволяют указать один или несколько элементов инициализаторами при инициализации коллекции кл задница, которая реализует IEnumerable или класс с добавлением метода расширения [...] "

'Надеюсь, это поможет.

+0

Ключевым моментом здесь является предоставление параметра типа, из которого можно вывести общий тип. Нет необходимости вводить его в метод расширения; он отлично работает, используя тот же подход в классе 'FooCollection()', как это было первоначально опубликовано в вопросе. (Хотя, если единственной причиной для подкласса типа является запись метода 'Add()', то, возможно, метод расширения лучше ... но это не является обязательным, просто чтобы дать ответ на вопрос и усложнить ответ). –

+0

Ну, я тебя слышу. Но вопрос OP немного неясен, поэтому я использовал только разумную догадку; поэтому, возможно, я должен был заметить заранее: называть «коллекцию» то, что не одно (как это больше похоже на карту, на самом деле), сбивает с толку. Итак, я полагал, что фокус OP заключался в том, чтобы получать прибыль от синтаксического сахара инициализатора коллекции и вводить тип, если только для кода инициализации, более компактного для записи и чтения, в то же время позволяя также использовать общие ограничения, независимо от того, какие функции класса могут быть в противном случае, и но не требуя использования дженериков в самом классе. – YSharp

+0

_ «вызов« коллекции »что-то, что не одно» _ - я не знаю, что это касается сделанного вами ответа (т. Е. Предпочитая отказаться от исходного кода вместо вашего собственного нового примера) но мне кажется, что вы раскалываете волосы слишком мелко. Словарь 'объявляется в пространстве имен' System.' ** 'Collections' ** .Generic', извлекается из синтаксиса инициализатора C# ** ** ** и даже реализуется' ICollection '. Мне кажется, что это какая-то коллекция для меня, и, конечно же, не совсем другая, чтобы оправдать изменение самого вопроса для вашего ответа. –