33 votes

Étant donné une instance de type, comment obtenir un nom de type générique en C #?

Étant donné un type générique, y compris

 List<string>
Nullable<Int32>
 

comment obtenir un nom générique pour C #?

 var t = typeof(Nullable<DateTime>);    
var s = t.GetGenericTypeDefinition().Name + "<" + t.GetGenericArguments()[0].Name + ">";
 

Cela donne

 "Nullable`1<DateTime>"
 

, Mais, j'ai besoin

 "Nullable<DateTime>"
 

.

68voto

Aaronaught Points 73049

Je vous vois déjà accepté une réponse, mais honnêtement, la réponse ne va pas être assez pour le faire de manière fiable si vous combinez ce qui est là avec ce que vous avez déjà écrit. Il est sur la bonne voie, mais ton code ne fonctionne que pour les types génériques avec exactement un paramètre générique, et il ne fonctionne que lorsque le paramètre de type générique lui-même n'est pas générique!

C'est une fonction (écrit comme une extension de la méthode) qui doit fonctionner dans tous les cas:

public static class TypeExtensions
{
    public static string ToGenericTypeString(this Type t)
    {
        if (!t.IsGenericType)
            return t.Name;
        string genericTypeName = t.GetGenericTypeDefinition().Name;
        genericTypeName = genericTypeName.Substring(0,
            genericTypeName.IndexOf('`'));
        string genericArgs = string.Join(",",
            t.GetGenericArguments()
                .Select(ta => ToGenericTypeString(ta)).ToArray());
        return genericTypeName + "<" + genericArgs + ">";
    }
}

Cette fonction est récursive et coffre-fort. Si vous l'exécutez sur cette entrée:

Console.WriteLine(
    typeof(Dictionary<string, List<Func<string, bool>>>)
    .ToGenericTypeString());

Vous obtenez ceci (correcte) de sortie:

Dictionary<String,List<Func<String,Boolean>>>

6voto

yo hal Points 1949

Alors que la solution retenue est bon pour juste le nom ou pour un non imbriquées nom complet (en remplaçant le nom de nom complet comme dans @Ose E de la réponse), mais pour les types imbriqués il ne sera toujours pas de travail et pas pour les tableaux de types génériques.

Voici donc une solution qui fonctionne, (mais notez que cette solution ne les arguments, seulement si tous les arguments sont ensemble, et en d'autres termes, même si le déclarant type a fourni le type de arguemts, aussi longtemps que le plus intime de type générique n'a pas, il ne sera toujours pas mis en évidence même pour la base).

    public static string ToGenericTypeString(this Type t, params Type[] arg)
    {
        if (t.IsGenericParameter || t.FullName == null) return t.Name;//Generic argument stub
        bool isGeneric = t.IsGenericType || t.FullName.IndexOf('`') >= 0;//an array of generic types is not considered a generic type although it still have the genetic notation
        bool isArray = !t.IsGenericType && t.FullName.IndexOf('`') >= 0;
        Type genericType = t;
        while (genericType.IsNested && genericType.DeclaringType.GetGenericArguments().Count()==t.GetGenericArguments().Count())//Non generic class in a generic class is also considered in Type as being generic
        {
            genericType = genericType.DeclaringType;
        }
        if (!isGeneric) return t.FullName.Replace('+', '.');

        var arguments = arg.Any() ? arg : t.GetGenericArguments();//if arg has any then we are in the recursive part, note that we always must take arguments from t, since only t (the last one) will actually have the constructed type arguments and all others will just contain the generic parameters
        string genericTypeName = genericType.FullName;
        if (genericType.IsNested)
        {
            var argumentsToPass = arguments.Take(genericType.DeclaringType.GetGenericArguments().Count()).ToArray();//Only the innermost will return the actual object and only from the GetGenericArguments directly on the type, not on the on genericDfintion, and only when all parameters including of the innermost are set
            arguments = arguments.Skip(argumentsToPass.Count()).ToArray();
            genericTypeName = genericType.DeclaringType.ToGenericTypeString(argumentsToPass) + "." + genericType.Name;//Recursive
        }
        if (isArray)
        {
            genericTypeName = t.GetElementType().ToGenericTypeString() + "[]";//this should work even for multidimensional arrays
        }
        if (genericTypeName.IndexOf('`') >= 0)
        {
            genericTypeName = genericTypeName.Substring(0, genericTypeName.IndexOf('`'));
            string genericArgs = string.Join(",", arguments.Select(a => a.ToGenericTypeString()).ToArray());
                //Recursive
            genericTypeName = genericTypeName + "<" + genericArgs + ">";
            if (isArray) genericTypeName += "[]";
        }
        if (t != genericType)
        {
            genericTypeName += t.FullName.Replace(genericType.FullName, "").Replace('+','.');
        }
        if (genericTypeName.IndexOf("[") >= 0 && genericTypeName.IndexOf("]") != genericTypeName.IndexOf("[") +1) genericTypeName = genericTypeName.Substring(0, genericTypeName.IndexOf("["));//For a non generic class nested in a generic class we will still have the type parameters at the end 
        return genericTypeName;
    }

-1voto

Osa E Points 464

Ajout mineur à @Aaronaught

 public string ToGenericTypeString(Type t)
{
    if (!t.IsGenericType)
        return t.FullName;
    string genericTypeName = t.GetGenericTypeDefinition().FullName;
    genericTypeName = genericTypeName.Substring(0,
        genericTypeName.IndexOf('`'));
    string genericArgs = string.Join(",",
        t.GetGenericArguments()
            .Select(ta => ToGenericTypeString(ta)).ToArray());
    return genericTypeName + "<" + genericArgs + ">";
}
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X