2009-06-09 3 views
172

В C# можно ли преобразовать переменную типа объекта в переменную типа T, где T определен в переменной типа?Кастинг переменной с использованием переменной типа

+10

не строго по теме, но вы, кажется, достаточно нечеткими о том, что «слепок» означает, что это может быть хорошая идея, чтобы точно понять, что цель и семантика оператора литого является. Вот хороший старт: http://blogs.msdn.com/ericlippert/archive/2009/03/19/representation-and-identity.aspx –

+0

Спасибо за ссылку в блоге. Похоже на интересное чтение. – theringostarrs

+6

Выбранный ответ не является ответом на этот вопрос –

ответ

138

Конечно, вы можете здесь как простой (предположим, что это слепок T-типа) литые и, если удобно и просто (предположим, что мы можем преобразовать это T) преобразования:

public T CastExamp1<T>(object input) { 
    return (T) input; 
} 

public T ConvertExamp1<T>(object input) { 
    return (T) Convert.ChangeType(input, typeof(T)); 
} 

Редактировать:

Некоторые люди в комментариях говорят, что этот ответ не отвечает на вопрос. Но линия (T) Convert.ChangeType(input, typeof(T)) обеспечивает решение. Метод Convert.ChangeType пытается преобразовать любой объект в тип, предоставленный в качестве второго аргумента.

Я написал ответ с помощью дженериков, потому что я думаю, что это очень вероятный признак запаха кода, если вы хотите отличить a something до a something else без обработки фактического типа. С соответствующими интерфейсами, которые не должны быть необходимы в 99,9% случаев. Возможно, есть несколько крайних случаев, когда дело доходит до размышлений о том, что это имеет смысл, но я бы рекомендовал избежать этих случаев.

+0

Как вы это делаете, мой компилятор C# жалуется на вход «возврат (T)». –

+2

ConvertExamp1 работал потрясающе для меня. Спасибо, Zyphrax! – PICyourBrain

+82

Я не знаю, как это помогает OP. У нее есть переменная типа, а не 'T' как таковая. – nawfal

6

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

Что вы могли бы сделать после броска? Вы не знаете тип, поэтому не могли бы вызывать на нем какие-либо методы. Не было бы никакой особой вещи, которую вы могли бы сделать. В частности, это может быть полезно, если вы знаете, возможные типы во время компиляции, брось его вручную и обрабатывать каждый случай отдельно if заявления:

if (type == typeof(int)) { 
    int x = (int)obj; 
    DoSomethingWithInt(x); 
} else if (type == typeof(string)) { 
    string s = (string)obj; 
    DoSomethingWithString(s); 
} // ... 
+1

Не могли бы вы объяснить, что яснее в отношении моего вопроса? – theringostarrs

+0

Что я пытаюсь объяснить, что вы могли бы сделать после этого? Вы не можете многое сделать, поскольку компилятор C# требует, чтобы статическая типизация могла делать полезную вещь с объектом. –

+0

Вы правы. Я знаю ожидаемые типы двух переменных, которые отправляются методу как тип «объект». Я хочу использовать ожидаемые типы, хранящиеся в переменных, и добавлять их в коллекцию. Гораздо проще разветвляться по типу и попробовать обычные ошибки при попадании и уловить. – theringostarrs

6

Как вы могли бы сделать это? Вам нужна переменная или поле типа T, где вы можете сохранить объект после трансляции, но как вы можете получить такую ​​переменную или поле, если знаете T только во время выполнения? Итак, нет, это невозможно.

Type type = GetSomeType(); 
Object @object = GetSomeObject(); 

??? xyz = @object.CastTo(type); // How would you declare the variable? 

xyz.??? // What methods, properties, or fields are valid here? 
+3

Если вы используете общий класс, который определяет метод с возвращаемым значением типа T, вам может понадобиться сделать это. Например. разбор строки на экземпляр T и возвращение этого. –

+1

Это правильный ответ, к сожалению ... – Thomas

+7

К счастью, это не правильный ответ. См. Ответ maulik13. – rushinge

1
public bool TryCast<T>(ref T t, object o) 
{ 
    if (
     o == null 
     || !typeof(T).IsAssignableFrom(o.GetType()) 
     ) 
     return false; 
    t = (T)o; 
    return true; 
} 
52

В других ответах не упоминается «динамический» тип. Таким образом, чтобы добавить еще один ответ, вы можете использовать «динамический» тип для хранения результирующего объекта без необходимости преобразовывать преобразованный объект со статическим типом.

dynamic changedObj = Convert.ChangeType(obj, typeVar); 
changedObj.Method(); 

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

+8

Это правильный ответ. Без динамического ключевого слова typeof (changedObj) является «объект». С ключевым словом dynamic оно работает безупречно, а typeof (changedObject) правильно отражает тот же тип, что и typeVar. Кроме того, вам не нужно использовать (T), который вы не можете сделать, если не знаете тип. – rushinge

+0

У меня есть исключение «Объект должен реализовать IConvertible» при использовании этого решения. Любая помощь? –

+0

@NuriTasdemir Трудно сказать, но я считаю, что преобразование, которое вы делаете, невозможно без IConvertible. Каковы типы, связанные с конверсией? – maulik13

1

еще чище:

public static bool TryCast<T>(ref T t, object o) 
    { 
     if (!(o is T)) 
     { 
      return false; 
     } 

     t = (T)o; 
     return true; 
    } 
12

Вот мой метод, который не использует дженерики:

//using System; 
//using System.Linq.Expressions; 
//using System.Reflection; 
public static object Cast(object obj, Type t) 
{ 
    try 
    { 
     var param = Expression.Parameter(obj.GetType()); 
     return Expression.Lambda(Expression.Convert(param, t), param) 
       .Compile().DynamicInvoke(obj); 
    } 
    catch (TargetInvocationException ex) 
    { 
     throw ex.InnerException; 
    }   
} 

Пожалуйста, обратите внимание, что это только выполняет кастинг на данные и базового типа. Возвращаемое значение остается object и должно использоваться как: dynamic d = Cast(obj, t);, потому что компилятор все еще не может знать его фактический тип.

Приведенный выше код является невыразимо неэффективным. Он создает новый метод внутри динамической сборки каждый раз, когда он вызывается. Другим недостатком является то, что он должен знать тип объекта, на который делается бросок.

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

//requres a reference to Microsoft.CSharp.dll 
using Microsoft.CSharp.RuntimeBinder; 
using System; 
using System.Collections.Generic; 
using System.Runtime.CompilerServices; 

public static class TypeCastHelper 
{ 
    private static Type CastType = typeof(CastClass<>); 
    private static Dictionary<Type, Func<object, object>> Cache 
     = new Dictionary<Type, Func<object, object>>(); 

    private static class CastClass<T> 
    { 
     private static CallSite<Func<CallSite, object, T>> Site; 

     static CastClass() 
     { 
      Site = CallSite<Func<CallSite, object, T>> 
       .Create(Microsoft.CSharp.RuntimeBinder.Binder 
       .Convert(CSharpBinderFlags.ConvertExplicit, 
       typeof(T), CastType)); 
     } 

     public static object Cast(object o) 
     { 
      try 
      { 
       return Site.Target.Invoke(Site, o); 
      } 
      catch (RuntimeBinderException e) 
      { 
       throw new InvalidCastException(e.Message); 
      } 
     } 
    } 

    private static Func<object, object> MakeCastDelegate(Type t) 
    { 
     return (Func<object, object>)CastType.MakeGenericType(t) 
      .GetMethod("Cast") 
      .CreateDelegate(typeof(Func<object, object>)); 
    } 

    public static Func<object, object> GetCastDelegate(Type t) 
    { 
     Func<object, object> d; 
     if (!Cache.TryGetValue(t, out d)) 
     { 
      d = MakeCastDelegate(t); 
      Cache.Add(t, d); 
     } 
     return d; 
    } 

    public static object Cast(object o, Type t) 
    { 
     return GetCastDelegate(t).Invoke(o); 
    } 
} 
+2

Требуется '' 'using System.Linq.Expressions;' '' –

Смежные вопросы