Приведение переменной с использованием переменной типа - PullRequest
233 голосов
/ 10 июня 2009

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

Ответы [ 10 ]

165 голосов
/ 18 июля 2009

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

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

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

Edit:

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

Например:

Type intType = typeof(Int32);
object value1 = 1000.1;

// Variable value2 is now an int with a value of 1000
object value2a = Convert.ChangeType(value1, intType);
int value2b = Convert.ChangeType(value1, intType);

// Variable value3 is now an int with a value of 1000
dynamic value3 = Convert.ChangeType(value1, intType);

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

92 голосов
/ 13 марта 2015

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

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

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

19 голосов
/ 13 июля 2015

Вот мой метод приведения объекта, но не к переменной общего типа, а к System.Type динамически:

Я создаю лямбда-выражение во время выполнения, используя System.Linq.Expressions, типа Func<object, object>, который распаковывает свой ввод, выполняет желаемое преобразование типа, а затем дает результат в штучной упаковке. Новый нужен не только для всех типов, которые подвергаются касту, но также и для типов, которые кастуются (из-за шага распаковки). Создание этих выражений отнимает много времени из-за рефлексии, компиляции и динамического построения метода, который делается под капотом. К счастью, после создания выражения можно вызывать повторно и без больших накладных расходов, поэтому я кеширую каждое из них.

private static Func<object, object> MakeCastDelegate(Type from, Type to)
{
    var p = Expression.Parameter(typeof(object)); //do not inline
    return Expression.Lambda<Func<object, object>>(
        Expression.Convert(Expression.ConvertChecked(Expression.Convert(p, from), to), typeof(object)),
        p).Compile();
}

private static readonly Dictionary<Tuple<Type, Type>, Func<object, object>> CastCache
= new Dictionary<Tuple<Type, Type>, Func<object, object>>();

public static Func<object, object> GetCastDelegate(Type from, Type to)
{
    lock (CastCache)
    {
        var key = new Tuple<Type, Type>(from, to);
        Func<object, object> cast_delegate;
        if (!CastCache.TryGetValue(key, out cast_delegate))
        {
            cast_delegate = MakeCastDelegate(from, to);
            CastCache.Add(key, cast_delegate);
        }
        return cast_delegate;
    }
}

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

Обратите внимание, что это не волшебство. Приведение не происходит в коде, как это происходит с ключевым словом dynamic, конвертируются только базовые данные объекта. Во время компиляции нам все еще приходится тщательно выяснять, какого типа может быть наш объект, что делает это решение непрактичным. Я написал это как хак для вызова операторов преобразования, определенных произвольными типами, но, возможно, кто-нибудь найдет лучший вариант использования.

7 голосов
/ 10 июня 2009

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

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

if (type == typeof(int)) {
    int x = (int)obj;
    DoSomethingWithInt(x);
} else if (type == typeof(string)) {
    string s = (string)obj;
    DoSomethingWithString(s);
} // ...
6 голосов
/ 10 июня 2009

Как ты мог это сделать? Вам нужна переменная или поле типа 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?
2 голосов
/ 27 августа 2018

Когда дело доходит до приведения типа Enum:

private static Enum GetEnum(Type type, int value)
    {
        if (type.IsEnum)
            if (Enum.IsDefined(type, value))
            {
                return (Enum)Enum.ToObject(type, value);
            }

        return null;
    }

И ты назовешь это так:

var enumValue = GetEnum(typeof(YourEnum), foo);

Это было важно для меня в случае получения значения атрибута Description нескольких типов перечисления по значению int:

public enum YourEnum
{
    [Description("Desc1")]
    Val1,
    [Description("Desc2")]
    Val2,
    Val3,
}

public static string GetDescriptionFromEnum(Enum value, bool inherit)
    {
        Type type = value.GetType();

        System.Reflection.MemberInfo[] memInfo = type.GetMember(value.ToString());

        if (memInfo.Length > 0)
        {
            object[] attrs = memInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), inherit);
            if (attrs.Length > 0)
                return ((DescriptionAttribute)attrs[0]).Description;
        }

        return value.ToString();
    }

, а затем:

string description = GetDescriptionFromEnum(GetEnum(typeof(YourEnum), foo));
string description2 = GetDescriptionFromEnum(GetEnum(typeof(YourEnum2), foo2));
string description3 = GetDescriptionFromEnum(GetEnum(typeof(YourEnum3), foo3));

В качестве альтернативы (лучший подход) такое приведение может выглядеть так:

 private static T GetEnum<T>(int v) where T : struct, IConvertible
    {
        if (typeof(T).IsEnum)
            if (Enum.IsDefined(typeof(T), v))
            {
                return (T)Enum.ToObject(typeof(T), v);
            }

        throw new ArgumentException(string.Format("{0} is not a valid value of {1}", v, typeof(T).Name));
    }
0 голосов
/ 14 июня 2019

После того как я не нашел ничего, что можно обойти, исключение «Объект должен реализовывать IConvertible» при использовании ответа Zyphrax (за исключением реализации интерфейса).

Использование Nuget-пакета Newtonsoft.Json ...

var castedObject = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(myObject), myType);
0 голосов
/ 17 октября 2018

Если вам нужно приводить объекты во время выполнения, не зная типа назначения, вы можете использовать отражение для создания динамического преобразователя.

Это упрощенная версия (без кеширования сгенерированного метода):

    public static class Tool
    {
            public static object CastTo<T>(object value) where T : class
            {
                return value as T;
            }

            private static readonly MethodInfo CastToInfo = typeof (Tool).GetMethod("CastTo");

            public static object DynamicCast(object source, Type targetType)
            {
                return CastToInfo.MakeGenericMethod(new[] { targetType }).Invoke(null, new[] { source });
            }
    }

тогда вы можете назвать это:

    var r = Tool.DynamicCast(myinstance, typeof (MyClass));
0 голосов
/ 06 июня 2015

даже чище:

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

        t = (T)o;
        return true;
    }
0 голосов
/ 24 января 2013
public bool TryCast<T>(ref T t, object o)
{
    if (
        o == null
        || !typeof(T).IsAssignableFrom(o.GetType())
        )
        return false;
    t = (T)o;
    return true;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...