web-dev-qa-db-fra.com

Lancer une variable à l'aide d'une variable de type

En C #, puis-je convertir une variable de type objet en une variable de type T où T est défini dans une variable Type?

242
theringostarrs

Bien sûr, vous pouvez utiliser à la fois un casting simple (supposons qu'il s'agisse d'un casting de type T) et, le cas échéant, un convertisseur (supposons que nous pouvons le convertir en T):

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

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

Edit:

Certaines personnes dans les commentaires disent que cette réponse ne répond pas à la question. Mais la ligne (T) Convert.ChangeType(input, typeof(T)) fournit la solution. La méthode Convert.ChangeType essaie de convertir tout objet en type fourni comme deuxième argument.

Par exemple:

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

J'ai écrit la réponse avec des génériques, car je pense que c'est très probablement un signe de bonne odeur de code lorsque vous souhaitez transtyper a something en a something else sans gérer un type réel. Avec des interfaces appropriées, cela ne devrait pas être nécessaire 99,9% du temps. Il y a peut-être quelques cas dans Edge quand il s'agit de penser que cela pourrait avoir un sens, mais je recommanderais d'éviter ces cas.

174
Zyphrax

Les autres réponses ne mentionnent pas le type "dynamique". Ainsi, pour ajouter une réponse supplémentaire, vous pouvez utiliser le type "dynamique" pour stocker votre objet résultant sans avoir à convertir l'objet converti avec un type statique.

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

Gardez à l'esprit qu'avec l'utilisation de "dynamique", le compilateur ignore la vérification de type statique qui pourrait introduire des erreurs d'exécution si vous ne faites pas attention.

98
maulik13

Voici ma méthode pour convertir un objet mais pas en une variable de type générique, mais plutôt en un System.Type dynamiquement:

Je crée une expression lambda au moment de l'exécution en utilisant System.Linq.Expressions, de type Func<object, object>, qui désencapsule son entrée, effectue la conversion de type souhaitée puis donne le résultat encadré. Un nouveau est nécessaire non seulement pour tous les types sur lesquels un casting est effectué, mais également pour les types qui sont projetés (en raison de l'étape de déballage). La création de ces expressions prend beaucoup de temps, en raison de la réflexion, de la compilation et du développement dynamique de méthodes effectué sous le capot. Heureusement, une fois créées, les expressions peuvent être invoquées de manière répétée et sans surcharge, alors je les cache.

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

Notez que ce n'est pas magique. La conversion ne se produit pas dans le code, comme c'est le cas avec le mot clé dynamic, seules les données sous-jacentes de l'objet sont converties. Au moment de la compilation, il nous reste à déterminer minutieusement le type exact de notre objet, ce qui rend cette solution peu pratique. J'ai écrit ceci comme un hack pour appeler des opérateurs de conversion définis par des types arbitraires, mais peut-être que quelqu'un peut trouver un meilleur cas d'utilisation.

19
balage

En mettant la boxe et le déballage de côté pour plus de simplicité, aucune action d’exécution particulière n’est impliquée dans la transposition le long de la hiérarchie des héritages. C'est principalement une chose de temps de compilation. Essentiellement, une distribution demande au compilateur de traiter la valeur de la variable comme un autre type.

Que pourriez-vous faire après le casting? Vous ne connaissez pas le type, vous ne pourrez donc appeler aucune méthode dessus. Il n'y aurait rien de spécial à faire. Spécifiquement, cela ne peut être utile que si vous connaissez les types possibles au moment de la compilation, le convertissez manuellement et gérez chaque cas séparément avec les instructions if:

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

Comment pouvez vous faire ça? Vous avez besoin d'une variable ou d'un champ de type T où vous pouvez stocker l'objet après la conversion, mais comment pouvez-vous avoir une telle variable ou un tel champ si vous ne connaissez T que lors de l'exécution? Donc non, ce n'est pas possible.

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

Quand il s’agit de choisir un type 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;
    }

Et vous l'appelerez comme ça:

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

Cela était essentiel pour moi dans le cas où j'obtenais la valeur d'attribut Description de plusieurs types d'énum en valeur 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();
    }

puis:

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

Alternativement (meilleure approche), un tel casting pourrait ressembler à ça:

 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

Après ne rien trouver pour contourner l'exception "Object doit implémenter IConvertible" lors de l'utilisation de la réponse de Zyphrax (sauf pour l'implémentation de l'interface) .. J'ai essayé quelque chose d'un peu non conventionnel et j'ai travaillé pour ma situation.

Utilisation du package de nuget 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