C # Коллекция с использованием универсальных типов или интерфейса - PullRequest
2 голосов
/ 19 августа 2010

У меня есть набор классов (MyClass1, MyClass2, MyClass3), которые реализуют интерфейс (IMyClass).

Я заинтересован в создании метода для заполнения и возврата списка (Collection.Generics.Lists<>) объектовкоторые имеют общий интерфейс MyClassX (List<IMyClass>).Но как?

У каждого класса есть конструктор MyClassX, который делает что-то свое.Поэтому я не могу создать BaseClass и использовать общий конструктор для всех.

По интерфейсу я не могу сделать:

public List<IMyClass> Fill(string[] ItemsPassedByParam, IMyClass InstanceOfMyClassX)
    List MyList = new List<IMyClass>();
    foreach (item in ItemsPassedByParam)
    {
        MyList.Add (new IMyClass (item)); //can't do new from an Interface!!
    }
}

Также я попытался использовать тип generics,TMyClass в следующем примере, но ни один из них не сработал для меня (хотя у меня нет очень глубоких представлений об универсальных типах):

public List<TMyClass> Fill(string[] ItemsPassedByParam, Type TypeOfMyClassX)
    List MyList = new List <TMyClass> ();
    foreach (item in ItemsPassedByParam)
    {
        MyList.Add (new TMyClass (item)); //can't do new from a Generic Type!!
    }
}

Я также пытался экстернализировать код внутри Конструктора MyClassX в методе, но я не могу вызвать метод, поскольку объект de не инициализирован (нет new ()).

В этих случаях, какое решение?

Заранее спасибо!

Ответы [ 4 ]

7 голосов
/ 19 августа 2010

Звучит так, будто вы хотите, чтобы метод Fill создал List<T> из переданных строковых значений, где T является производным от IMyClass.

Вам нужно передать фабричную функцию в метод Fill, который позволяет создавать IMyClass производные экземпляры.

public List<IMyClass> Fill(
  string[] ItemsPassedByParam, 
  Func<string,IMyClass> createFunc)

  List MyList = new List<IMyClass>();
  foreach (item in ItemsPassedByParam) {
    MyList.Add(createFunc(item));        
  }
}

Затем в месте, где вы звоните Fill, вы можете выбрать, какой из производных IMyClass использовать

string[] items = ...;
List<IMyClass> list1 = Fill(items, item => new MyClass1(item));
List<IMyClass> list2 = Fill(items, item => new MyClass2(item));
2 голосов
/ 19 августа 2010

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

public List<TMyClass> Fill(string[] ItemsPassedByParam) 
    where TMyClass : IMyClass, new() {
    ...
        IMyClass inst = new TMyClass();
        inst.SetItem(item);
        myList.Add(inst);
    ...
}

Вызов new TMyClass имеет тот же эффект, что и вызов Activator.CreateInstance<TMyClass>() Универсальный CreateInstance не допускает аргументы дляпередаваться в конструктор, но вы можете передавать их с помощью не-родового CreateInstance.

public List<TMyClass> Fill(string[] ItemsPassedByParam) {
    ...
        myList.Add((IMyList)Activator.CreateInstance(typeof(TMyClass), item));
    ...
}
2 голосов
/ 19 августа 2010

Лучшим вариантом здесь является создание IMyClassFactory, который обрабатывает получение параметра, выяснение, какой конкретный тип создать, вызвать конструктор и вернуть его:

public class IMyClassFactory
{
    public static IMyClass CreateInstance(string item)
    {
        switch(item)
        {
            case "SomeValue":
            case "SomeValue2":
                return new MyClass1(item);
            case "SomeOtherValue":
                return new MyClass2(item);
        }
    }
}

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

После того, как у вас есть фабрика, вы можете использовать оригинальный метод, используя фабрику:

public List<IMyClass> Fill(string[] ItemsPassedByParam)
{
    List MyList = new List<IMyClass>();

    foreach (item in ItemsPassedByParam)
    {
        MyList.Add(IMyClassFactory.CreateInstance(item));
    }
}
1 голос
/ 19 августа 2010

Может быть, вы можете просто использовать здесь фабричный шаблон, в зависимости от того, как вы определите, какой тип нужен.

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

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

public static T BuildOne(SomeParamType myParam)
{
  ConstructorInfo constrInfo = typeof(T).GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, new Type[]{typeof(SomeParamType)}, null);
  return (T)constrInfo.Invoke(new object[]{myParam});
}
...