2011-09-21 13 views
8

Chcę przetestować, czy dany object może być rzutowany na dane Type.C#: Jak wykonać operację "as" z Type

W tym scenariuszu mam obiektu, a Type reprezentujący co typ chcę oddać go do:

public function FooBar(..., object data, Type expected) { 
    ... 
    var unboxedData = ? 
    if (unboxedData == null) { 
     .... 
    } 
    ... 
} 

Jak mogę rzucić data do rodzaju type reprezentuje?

Zasadniczo, chcę to zrobić:

var unboxedData = data as Type; 

... ale oczywiście nie można wykorzystać Type z oświadczeniem as, więc co mam zrobić?

+4

Chcesz * przetestować * lub chcesz * rzucić *? – CesarGon

Odpowiedz

6

Edycja 2: Zamierzam powiedzieć, że nie jest to możliwe bez refleksji lub generycznych. Dzięki odbiciu nie masz kontroli kompilacji i musisz użyć odbicia (lub dynamic), aby wywołać metody/właściwości obiektu. W przypadku leków generycznych nie można użyć obiektu typu, aby dostać się samemu. Wybierz. Czy możliwe jest zreorganizowanie kodu wywołującego, aby umożliwić generyczne?


Jeśli wolno, to może być łatwiej obchodzić się z metody rodzajowe:

public resultType FooBar<T>(..., object data) { 
    ... 
    T unboxedData = (T)data; 
    ... 
} 

Edit: Ponadto, można użyć data as T jeśli zawierają rodzajowe typu ograniczenie: where T : class:

public something FooBar<T>(..., object data) 
    where T : class 
{ 
    ... 
    T unboxedData = data as T; 
    if (unboxedData == null) { 
     ... 
    } 
    ... 
} 
+2

A co, jeśli nie implementuje IConvertible? – IEHaterNumber1

+0

Właściwie, miałem 'Convert.ChangeType', ponieważ pamiętałem błędy kompilatora podczas próby bezpośredniego rzutowania bez tego niepotrzebnego wywołania. Właśnie testowałem i "(T) dane" działają dobrze, więc zmieniłem to na tamto. Możesz także wykonać 'dane jako T', jeśli dodasz ogólne ograniczenie typu' where T: class'. – mellamokb

+0

Wydaje się, że to nie rozwiązuje problemu, ponieważ zaczyna się od zmiennej Typ. zobacz: http://stackoverflow.com/questions/266115/pass-an-instantiated-system-type-as-a-type-parameter-for-a-generic-class, których nie możesz przekazać w instancji Type rodzajowy. – deepee1

3

... ale oczywiście nie można użyć opcji Wpisz jako tatement, więc co mam zrobić?

Co ważne, w ten sposób nie można używać numeru var. Nie ma tu nic do zyskania.

Można Test czy to odpowiedni rodzaj z

if (expected.IsInstanceOfType(data)) 

Ale wtedy jeszcze nie można zapisać żadnego godnego kod dostępu do właściwości lub metody na data.

2

C# dostarcza jako słowo kluczowe jako szybkie, aby określić w czasie wykonywania, czy dany typ jest kompatybilny z innym. Podczas używania słowa kluczowego as można określić zgodność, zwracając wartość zerową. Należy rozważyć następujące kwestie:

Hexagon hex2 = frank as Hexagon; 

if (hex2 == null) 
    Console.WriteLine("Sorry, frank is not a Hexagon..."); 

Oprócz słowa kluczowego as język C# udostępnia słowo kluczowe is w celu ustalenia, czy dwa elementy są zgodne. Jednak w odróżnieniu od słowa kluczowego as słowo kluczowe is zwraca wartość false, a nie odwołanie zerowe, jeśli typy są niekompatybilne.

if (emp is SalesPerson) 
{ 
    Console.WriteLine("{0} made {1} sale(s)!", emp.Name, 
               ((SalesPerson)emp).SalesNumber); 
} 
+2

Nie używasz "Type" w dowolnym miejscu. Wyraźnie definiujesz typ. – IEHaterNumber1

+1

cóż, używasz 'var', więc i tak nie musisz używać operatora" as ". Poza tym, jeśli chcesz przypisać, powiedz Typ a do zmiennej typu B, która dziedziczy po A, możesz użyć 'type a = type B as type A'. –

+2

@Pankaj Myślę, że źle rozumiesz: OP przechodzi w instancji klasy [.NET Type] (http://msdn.microsoft.com/en-us/library/system.type.aspx), która jest reprezentacja metadanych danego typu. OP zastanawia się, jak porównać typ obiektu z typem * reprezentowanym przez * a 'Type'. –

1
if (data.GetType() == t || data.GetType().IsSubclassOf(t)) 
{ 
//do your thing 
} 

powinien powiedzieć, czy to dokładnie lub podklasą (więc mogą być rzucane w niego).

0

Dobrze, rozglądając Znalazłem somthing ... How to check if implicit or explicit cast exists?

Bądź ostrożny, nie dały mu wiele testów, ale na pierwszy rzut oka wydaje się być obiecujące. Dużym minusem jest to, że zgłasza wyjątek, jeśli nie można przekonwertować go:

static bool isConvertableTo(object o, Type t) 
    { 
     try 
     { 
      var expr = Expression.Constant(o); 
      var res = Expression.Convert(expr, t); 
      return true; 
     } 
     catch { } 
     return false; 
    } 

Innym przydatnym związek z samego podejścia: Checking if a type supports an implicit or explicit type conversion to another type with .NET

1

Jest to dość trudne. Problem polega na tym, że var nie oznacza "wariantu". Działa to bardziej jak tymczasowy symbol zastępczy, który C# wypełnia rzeczywistym typem, gdy informacje o typie można wywnioskować z wyrażenia. unboxedData jest nadal bardzo mocno wpisaną zmienną. Jest to po prostu kompilator próbuje dowiedzieć się typ zamiast go wyraźnie określając go. Ważne jest, aby pamiętać, że pisanie nadal występuje w czasie kompilacji, a nie w czasie wykonywania.

Jeśli chcesz dynamicznie rzutować obiekt w czasie wykonywania, nie będziesz mógł użyć var ani żadnego innego specyfikatora typu betonu.

opcje są ograniczone do jednego z dwóch możliwych zgłoszeń:

  • obiektu
  • dynamiczny

podstawie tego, co myślę chcesz zrobić z unboxedData Podejrzewam dynamic jest trasa, którą chcesz iść, ponieważ pozwoliłaby ci wywołać dowolną metodę na celu Type.

Oto, co wymyśliłem.

public void FooBar(object value, Type expected) 
{ 
    dynamic unboxedData = expected.FromObject(value); 
    unboxedData.CallSomeMethodDefinedInTheTargetType(); // This will work. 
} 

Wymagana jest następująca metoda przedłużenia.

public static class TypeExtension 
{ 
    public static object FromObject(this Type target, object value) 
    { 
     var convertable = value as IConvertible; 
     if (convertable != null) 
     { 
      return convertable.ToType(target, null); 
     } 
     Type type = value.GetType(); 
     if (target.IsAssignableFrom(type)) 
     { 
      return value; 
     } 
     MethodInfo[] methods = type.GetMethods(BindingFlags.Static | BindingFlags.Public); 
     foreach (MethodInfo mi in methods) 
     { 
      if (mi.ReturnType == target) 
      { 
       try 
       { 
        return mi.Invoke(null, new object[] { value }); 
       } 
       catch (TargetInvocationException caught) 
       { 
        if (caught.InnerException != null) 
        { 
         throw caught.InnerException; 
        } 
        throw; 
       } 
      } 
     } 
     throw new InvalidCastException(); 
    } 
} 

Gips będzie działał, jeśli spełniony jest jeden z poniższych warunków.

  • Wartość do przekonwertowania implementuje IConvertible i zawiera ścieżkę konwersji do typu celu.
  • Wartość do przekonwertowania podklas typ docelowy.
  • Wartość do przekonwertowania określa explicit conversion operator w deklaracji klasy.
+0

Zamierzałem napisać odpowiedź, ale twoja jest znacznie lepsza. +1 – Davy8

Powiązane problemy