Najlepszy sposób na sprawdzenie, czy typ ogólny jest ciągiem znaków? (DO#)

95

Mam klasę ogólną, która powinna zezwalać na dowolny typ, prymitywny lub inny. Jedynym problemem jest używanie default(T). Kiedy wywołujesz default dla typu wartości lub ciągu, inicjalizuje go do rozsądnej wartości (takiej jak pusty ciąg). Kiedy wywołujesz default(T)obiekt, zwraca on null. Z różnych powodów musimy się upewnić, że jeśli nie jest to typ pierwotny, będziemy mieć domyślną instancję typu, a nie null. Oto próba 1:

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

Problem - łańcuch nie jest typem wartości, ale nie ma konstruktora bez parametrów. Tak więc obecne rozwiązanie to:

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

Ale to wydaje się bzdurą. Czy jest lepszy sposób na obsłużenie obudowy sznurka?

Rex M
źródło

Odpowiedzi:

162

Pamiętaj, że default (string) ma wartość null, a nie string.Empty. Możesz potrzebować specjalnego przypadku w swoim kodzie:

if (typeof(T) == typeof(String)) return (T)(object)String.Empty;
Matt Hamilton
źródło
2
Myślałem, że wypróbowałem to rozwiązanie wcześniej i nie zadziałało, ale musiałem zrobić coś głupiego. Dzięki za wskazanie wartości default (string) zwraca null, nie napotkaliśmy jeszcze z tego powodu błędu, ale to prawda.
Rex M
1
@Matt Hamilton: +1, ale powinieneś zaktualizować swoją odpowiedź, aby zwracała '(T) (obiekt) String.Empty' zgodnie z sugestią CodeInChaos, ponieważ typ zwracanej metody jest ogólny, nie możesz po prostu zwrócić ciągu.
VoodooChild
2
A co ze issłowem kluczowym? Czy to nie jest przydatne tutaj?
Naveed Butt
W tej chwili nie jest możliwe zastosowanie operatora is z typami ogólnymi i przypisaniem lub bezpośrednim instancją, prawda? Będzie to fajna funkcja
Juan Pablo Garcia Coello
14
if (typeof(T).IsValueType || typeof(T) == typeof(String))
{
     return default(T);
}
else
{
     return Activator.CreateInstance<T>();
}

Niesprawdzone, ale pierwsza rzecz, jaka przyszła mi do głowy.

FlySwat
źródło
4

Możesz użyć wyliczenia TypeCode . Wywołaj metodę GetTypeCode w klasach, które implementują interfejs IConvertible, aby uzyskać kod typu dla wystąpienia tej klasy. IConvertible jest implementowana przez Boolean, SByte, Byte, Int16, UInt16, Int32, UInt32, Int64, UInt64, Single, Double, Decimal, DateTime, Char i String, dzięki czemu można sprawdzić typy pierwotne przy użyciu tego. Więcej informacji na temat „ Generic Type Checking ”.

jfs
źródło
2

Osobiście lubię przeciążanie metod:

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();
  }
}
theoski
źródło
-6

Dyskusja na temat String nie działa tutaj.

Musiałem mieć następujący kod dla generycznych, aby działał -

   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>();
            } 
        } 

    }
Indygowiec
źródło
3
Testowanie Stringwedług nazwy, zwłaszcza bez uwzględnienia przestrzeni nazw, jest złe. Nie podoba mi się też sposób, w jaki się nawracasz.
CodesInChaos