C # - создание класса в зависимости от предоставленного интерфейса в универсальном классе - PullRequest
2 голосов
/ 20 ноября 2008

У меня есть универсальная функция, которая получает интерфейс как тип, теперь в одном условии я должен создать новый класс в зависимости от интерфейса. Я думал об этом, и одним из способов решения этой проблемы было бы использование IoC, но я надеялся, что будет другой путь, потому что IoC выглядит как избыточное убийство.

ниже - попытка использования шаблона посетителя:

public class RepositoryManager<T> : IRepositoryManager<T> where T : class, new()
{
    public T GetOrCreate(string id)
    {
        T item = (T)CreateNew(new T(), id);
        return item;
    }
}

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

У меня была еще одна идея, могу ли я сделать объявление where как или?

public class RepositoryManager<T> : IRepositoryManager<T> where T : class, Iabc or Ixyz, new()

Надеюсь, вопрос ясен:)

-Марк


Спасибо за ответ.

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

Класс RepositoryManager:

private static IMedicament CreateNew(IMedicament emptyType, string id)
{
    return new Medicament { Id = id };
}
private static IRefund CreateNew(IRefund emptyType, string id)
{
    return new Refund { Id = id };
}

RepositoryManager<Iabc> abcRepository = new RepositoryManager<Iabc>();
RepositoryManager<Ixyz> xyzRepository = new RepositoryManager<Ixyz>();

Iabc abc = abcRepository.GetOrCreate("12345");
Ixyz xyz = xyzRepository.GetOrCreate("12345");

, поэтому использование T item = (T)CreateNew(new T(), id); не будет работать, потому что я должен сказать, что T может быть типа Iabc или Ixyz, но когда я это делаю, я получаю следующую ошибку:

Вызов неоднозначен между следующими методами или свойствами: RepositoryManager<T>.CreateNew(IMedicament, string) и RepositoryManager<T>.CreateNew(IRefund, string)

Было бы неплохо, если бы я заработал на этом, кроме простого копирования кода несколько раз.

Ответы [ 6 ]

2 голосов
/ 20 ноября 2008

Я не уверен, что полностью понимаю вопрос, но раньше использовал что-то подобное.

public class Repository
{
    public T Create<T>(string id) where T : class
    {
        return Activator.CreateInstance(typeof(T), new[] { id }) as T;
    }
}
2 голосов
/ 20 ноября 2008

А почему это не работает?

public class RepositoryManager<T> : IRepositoryManager<T> where T : Ixyz, new()
{
    public T GetOrCreate(string id)
    {
        T item = (T)CreateNew(new T(), id);
        return item;
    }
}

Альтернативой, если вы не можете использовать new (), является передача делегата для создания объекта (или совместимого типа):

public class RepositoryManager<T> : IRepositoryManager<T> where T : Ixyz
{
    private Func<T> _tConstructor;

    public RepositoryManager(Func<T> tConstructor)
    {
      this._tConstructor = tConstructor;
    }

    public T GetOrCreate(string id, )
    {
        T item = (T)CreateNew(this._tConstructor(), id);
        return item;
    }
}
1 голос
/ 29 мая 2009

Как насчет того, чтобы IRefund и IMedicament реализовали общий интерфейс, у обоих из которых есть свойства Id?

1 голос
/ 20 ноября 2008

Если я понимаю, в чем ваш вопрос, в основном вы хотите сделать (в некотором смысле) то, что делает RhinoMocks, за исключением того, что RhinoMocks фактически динамически создает класс из интерфейса, где вы хотите использовать существующий класс.

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

http://ayende.com/projects/rhino-mocks/downloads.aspx

0 голосов
/ 25 августа 2010

Может быть, следующие могут работать для вас? 1) создать словарь, в котором тип - это ваш интерфейс (параметр T), а объект - это фиктивный экземпляр объекта, который можно создать, 2) пусть ваш фиктивный объект будет фабрикой новых объектов, реализующих T.

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

0 голосов
/ 20 ноября 2008

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

public void Method<T>()
{
  Type type = typeof(T);

  T newObject = (T)type.GetConstructor(new System.Type[] { }).Invoke(new object[] { });
}

В примере используется отражение. Если вам нужны параметры, вы можете помещать объекты в массивы, созданные в коде.

...