it-swarm.com.ru

Приведение переменной с использованием переменной типа

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

233
theringostarrs

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

163
Zyphrax

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

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

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

92
maulik13

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

19
balage

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

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

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

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

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

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

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

var castedObject = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(myObject), myType);
0
Curt
public bool TryCast<T>(ref T t, object o)
{
    if (
        o == null
        || !typeof(T).IsAssignableFrom(o.GetType())
        )
        return false;
    t = (T)o;
    return true;
}
0
user2008563