Подтвердить что ты не робот

С# - Добавление объектов, реализующих интерфейсы к словарю

У меня есть словарь:

private Dictionary<Type, IExample> examples;

У меня есть два класса, которые реализуют интерфейс:

public class Example1 : IExample
{
}

public class Example2 : IExample
{
}

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

public T GetExample<T>() where T : IExample
{
    // Return the signal if it exists
    if (examples.ContainsKey(typeof(T)))
    {
        IExample value;

        if (!examples.TryGetValue(typeof(T), out value))
        {
            // unable to get value
        }

        return (T)value;
    }

    // Stuck on this line here. How exactly do I instantiate a new example if it doesn't exist.
    examples.Add(typeof(T), new );

    return default(T);
}

Возможно ли это?

4b9b3361

Ответ 1

Вам нужно будет добавить ограничение типа общего типа для вашего общего метода для конструктора без параметров, а затем вы можете создать экземпляр типа T, например new T():

public T GetExample<T>() where T : IExample,class,new()
{
       IExample value;

       if (examples.TryGetValue(typeof(T), out value))
       {
           return (T)value;
       }


   T obj =  new T(); // create instance of T and use further down in code it reference
   examples.Add(typeof(T),obj );

   return obj ;
}

и return default(T); вернут null не новый экземпляр T, как для класса (Reference Types) значение по умолчанию null, я сомневаюсь, что вы хотите сделать return new T(); там, который создаст новый объект и вернет ссылку на него обратно вызывающему.

Ответ 2

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

Более гибкий подход принимает делегат, который создает отсутствующий объект:

public T GetExample<T>(Func<T> make) where T : IExample, class {
    IExample value;
    if (examples.TryGetValue(typeof(T), out value)) {
        return (T)value;
    }
    T res =  make();
    examples.Add(typeof(T), res);
    return res;
}

Таким образом, вызывающий объект управляет тем, как создавать новые объекты, когда нужный объект не кэшируется в examples. Ваш код обращается "по требованию", поэтому логика создания объекта остается скрытой в коде вызывающего абонента, например

var e = exampleFactory.GetExample<MyExample>(
    () => new MyExample(firstParameter, secondParameter)
);

Ответ 3

Вы можете использовать отражение для создания типичного типа:

var newInstance =  return Activator.CreateInstance<T>();

Как отметил Эшан, вам нужно добавить ограничение к типу T, чтобы он мог быть создан: where T: class.

Или добавьте ограничение new() where T: new(). Затем вы можете создать его как

var newInstance = new T();

Ответ 4

Вы можете использовать новое ограничение @EhsanSajjad и создать общий метод расширения для словаря:

public static class DictionaryExtensions
{
    public static TValue GetOrCreate<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key) where TValue : new()
    {
        var exists = dictionary.TryGetValue(key, out var value);

        if (!exists)
        {
            value = new TValue();

            dictionary.Add(key, value);
        }

        return value;
    }
}

out var будет работать только в том случае, если вы используете С# 7, иначе вам придется разделить строку.

Или даже лучше с дополнительным значением по умолчанию, которое вы можете передать:

public static class DictionaryExtensions
{
    public static TValue GetOrCreate<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, TValue defaultValue = null) where TValue : class, new()
    {
        var exists = dictionary.TryGetValue(key, out var value);

        if (!exists)
        {
            value = defaultValue ?? new TValue();

            dictionary.Add(key, value);
        }

        return value;
    }
}

Ответ 5

Развернувшись на ответе Эхсана, вот как я это сделаю:

public T GetExample<T>() where T : IExample,class,new()
{

    IExample value;

    if (!examples.TryGetValue(typeof(T), out value))
    {
        // Object doesn't exist. Create and add
        value = new T();
        examples.Add(typeof(T), value);
    }

    return (T)value;
}

Использование как ContainsKey, так и TryGetValue является избыточным.