Возврат нового экземпляра, а не нулевого экземпляра в обобщениях - PullRequest
4 голосов
/ 16 июля 2011

У меня есть класс, который извлекает объекты из изолированного хранилища. Если он не может найти рассматриваемый объект, он возвращает значение по умолчанию (T), которое будет нулевым, поскольку они являются ссылочными типами. Если возвращаемое значение равно нулю, я делаю простую проверку и назначаю новый экземпляр в вызывающей стороне, но я бы предпочел сделать это в логике хранилища.

Итак, мой вопрос, есть ли способ вернуть новый T, если у объекта есть пустой конструктор по умолчанию?

Ответы [ 5 ]

14 голосов
/ 16 июля 2011

Опция будет использовать ограничение "новый": http://msdn.microsoft.com/en-us/library/sd2w2ew5(v=vs.80).aspx

Вот так:

public T GetNewItem()
    where T: new()
{
    return new T();
}

Но наличие этого ограничения означает, что вы не можете использовать тип, у которого нет конструктора по умолчанию. Поэтому вы можете рассмотреть возможность использования System.Activator.CreateInstance, но помните, что это может вызвать исключение:

T createInstance<T>()
{
    try
    {
        return System.Activator.CreateInstance<T>();
    }
    catch (MissingMethodException exc)
    {
        return default(T);
    }
}

Таким образом, было бы неплохо узнать, поддерживает ли данный тип это в начале инициализации, способ сделать это следующим образом:

T createInstance<T>()
{
    System.Reflection.ConstructorInfo constructor = (typeof(T)).GetConstructor(System.Type.EmptyTypes);
    if (ReferenceEquals(constructor, null))
    {
        //there is no default constructor
        return default(T);
    }
    else
    {
        //there is a default constructor
        //you can invoke it like so:
        return (T)constructor.Invoke(new object[0]);
        //return constructor.Invoke(new object[0]) as T; //If T is class
    }
}

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

Func<T> getConstructor<T>()
{
    System.Reflection.ConstructorInfo constructor = (typeof(T)).GetConstructor(System.Type.EmptyTypes);
    if (ReferenceEquals(constructor, null))
    {
        return () => { return default(T); };
    }
    else
    {
        return () => { return (T)constructor.Invoke(new object[0]); };
    }
}

Пример того, как его использовать (скомпилировано с LinqPad):

void Main()
{
    Console.WriteLine(getConstructor<object>()());
    Console.WriteLine(getConstructor<int>()());
    Console.WriteLine(getConstructor<string>()());
    Console.WriteLine(getConstructor<decimal>()());
    Console.WriteLine(getConstructor<DateTime>()());
    Console.WriteLine(getConstructor<int?>()());
}

Вывод:

System.Object
0
null
0
01/01/0001 12:00:00 a.m.
null

Случай строки - это особый случай, поскольку он является ссылочным типом, он может иметь значение null и не имеет общедоступного конструктора по умолчанию, который вы получаете здесь вместо String.Empty. Обнуляемый тип также дает ноль.

3 голосов
/ 16 июля 2011

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

public class Foo<T> where T : new()
{
    // Now you can say T blah = new T();
}

Вы также можете вызвать Activator.CreateInstance<T>(), но он выдаст, если у типа нет правильного конструктора.

Я думаю, вам лучше документировать, что ваш метод возвращает null, если объект не может быть найден, и позволить вызывающему коду обрабатывать это условие так, как он считает нужным. Он будет в лучшем положении, чтобы знать, как действовать.

2 голосов
/ 16 июля 2011

Добавьте ограничение new() к вашему универсальному методу:

public T Create<T>() where T: class, new()
{
  return new T();
}
1 голос
/ 16 июля 2011

Это работает:

с использованием системы;

public class Test
{
    static T CreateT<T>(bool _new) where T: new()
    {
        if (_new) return new T(); else return default(T);
    }
    public static void Main()
    {
        var o = CreateT<object>(true);
    }
}
0 голосов
/ 21 апреля 2013

С Универсальный метод для создания экземпляра переменной любого типа (в том числе с нулевой структурой)?

public static T GetEmpty<T>() where T : new() //this constraint is important
{
    return (T)Activator.CreateInstance(Nullable.GetUnderlyingType(typeof(T)) ?? typeof(T));
}

Это работает и для обнуляемых структур. Для int? возвращается 0, а не ноль. Более быстрые подходы здесь.

...