Вычисление переменной с использованием переменной типа

В С# я могу применить переменную типа объекта к переменной типа T, где T определен в переменной типа?

Ответ 1

Конечно, вы можете сделать это как простое (предположим, это приведение типа 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 пытается преобразовать любой объект в тип, указанный в качестве второго аргумента.

Например:

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 else a something во a something else без обработки фактического типа. С правильными интерфейсами это не должно быть необходимо в 99,9% случаев. Возможно, есть несколько крайних случаев, когда речь заходит о том, что это может иметь смысл, но я бы рекомендовал избегать таких случаев.

Ответ 2

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

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

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

Ответ 3

Вот мой метод приведения объекта, но не к переменной общего типа, а к динамическому типу 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 ключевым словом, конвертируются только базовые данные объекта. Во время компиляции нам все еще приходится тщательно выяснять, какого типа может быть наш объект, что делает это решение непрактичным. Я написал это как хак для вызова операторов преобразования, определенных произвольными типами, но, возможно, кто-то найдет лучший вариант использования.

Ответ 4

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

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

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

Ответ 5

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

Ответ 6

Когда дело доходит до приведения типа 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));
    }

Ответ 7

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

Используя пакет Nugets Newtonsoft.Json...

var castedObject = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(myObject), myType);

Ответ 8

Вред, проблема в том, что у тебя нет T.

у вас есть только переменная типа.

Подсказка для MS, если бы вы могли сделать что-то вроде

TryCast<typeof(MyClass)>

если бы решить все наши проблемы.

Ответ 9

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

Ответ 10

еще чище:

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

        t = (T)o;
        return true;
    }

Ответ 11

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

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

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