Лучший способ проверить, является ли универсальный тип строкой? (C #)

95

У меня есть общий класс, который должен разрешать любой тип, примитивный или другой. Единственная проблема с этим - использование default(T). Когда вы вызываете default для типа значения или строки, он инициализирует его разумным значением (например, пустой строкой). Когда вы вызываете default(T)объект, он возвращает null. По разным причинам нам необходимо убедиться, что если это не примитивный тип, то у нас будет экземпляр типа по умолчанию, а не null. Вот попытка 1:

T createDefault()
{
    if(typeof(T).IsValueType)
    {
        return default(T);
    }
    else
    {
        return Activator.CreateInstance<T>();
    }
}

Проблема - строка не является типом значения, но у нее нет конструктора без параметров. Итак, текущее решение:

T createDefault()
{
    if(typeof(T).IsValueType || typeof(T).FullName == "System.String")
    {
        return default(T);
    }
    else
    {
        return Activator.CreateInstance<T>();
    }
}

Но это похоже на лабиринт. Есть ли лучший способ справиться со строкой?

Рекс М
источник

Ответы:

162

Имейте в виду, что по умолчанию (строка) имеет значение null, а не string.Empty. Вам может понадобиться особый случай в вашем коде:

if (typeof(T) == typeof(String)) return (T)(object)String.Empty;
Мэтт Гамильтон
источник
2
Я думал, что пробовал это решение раньше, но оно не сработало, но, должно быть, я сделал что-то глупое. И спасибо, что указали, что default (string) возвращает null, мы еще не столкнулись с ошибкой из-за этого, но это правда.
Rex M
1
@Matt Hamilton: +1, но вы должны обновить свой ответ, чтобы вернуть '(T) (object) String.Empty', как это было предложено CodeInChaos, поскольку тип возврата метода является общим, вы не можете просто вернуть строку.
VoodooChild
2
А как насчет isключевого слова? Разве это не нужно здесь?
Навид Батт
На данный момент невозможно применить оператор is с обобщениями и назначением или прямым экземпляром, не так ли? Это будет классная функция
Хуан Пабло Гарсия Коэльо
14
if (typeof(T).IsValueType || typeof(T) == typeof(String))
{
     return default(T);
}
else
{
     return Activator.CreateInstance<T>();
}

Не тестировал, но первое, что пришло в голову.

FlySwat
источник
4

Вы можете использовать перечисление TypeCode . Вызовите метод GetTypeCode для классов, реализующих интерфейс IConvertible, чтобы получить код типа для экземпляра этого класса. IConvertible реализуется с помощью Boolean, SByte, Byte, Int16, UInt16, Int32, UInt32, Int64, UInt64, Single, Double, Decimal, DateTime, Char и String, поэтому вы можете проверять примитивные типы, используя это. Дополнительная информация о « Проверка общего типа ».

jfs
источник
2

Лично мне нравится перегрузка методов:

public static class Extensions { 
  public static String Blank(this String me) {      
    return String.Empty;
  }
  public static T Blank<T>(this T me) {      
    var tot = typeof(T);
    return tot.IsValueType
      ? default(T)
      : (T)Activator.CreateInstance(tot)
      ;
  }
}
class Program {
  static void Main(string[] args) {
    Object o = null;
    String s = null;
    int i = 6;
    Console.WriteLine(o.Blank()); //"System.Object"
    Console.WriteLine(s.Blank()); //""
    Console.WriteLine(i.Blank()); //"0"
    Console.ReadKey();
  }
}
Теоски
источник
0

Я знаю, что это старый вопрос, но было обновление.

Начиная с C # 7.0, вы можете использовать isоператор для сравнения типов. Вам больше не требуется использовать typeofas в принятом ответе.

        public bool IsObjectString(object obj)
        {
            return obj is string;
        }

https://docs.microsoft.com/en-US/dotnet/csharp/language-reference/keywords/is

Armitxes
источник
-6

Обсуждение String здесь не работает.

Мне нужен был следующий код для дженериков, чтобы он работал -

   private T createDefault()
    { 

        {     
            if(typeof(T).IsValueType)     
            {         
                return default(T);     
            }
            else if (typeof(T).Name == "String")
            {
                return (T)Convert.ChangeType(String.Empty,typeof(T));
            }
            else
            {
                return Activator.CreateInstance<T>();
            } 
        } 

    }
Анил
источник
3
Тестирование Stringпо имени, особенно без учета пространства имен, - это плохо. И мне тоже не нравится, как вы обращаетесь.
CodesInChaos,