Учитывая экземпляр типа, как получить общее имя типа в C #? - PullRequest
30 голосов
/ 15 марта 2010

Дан универсальный тип, включая

List<string>
Nullable<Int32>

как мне получить общее имя для C #?

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

Это дает

"Nullable`1<DateTime>"

, но мне нужно

"Nullable<DateTime>"

.

Ответы [ 5 ]

58 голосов
/ 15 марта 2010

Я вижу, что вы уже приняли ответ, но, честно говоря, этого ответа будет недостаточно, чтобы сделать это надежно, если вы просто объедините то, что там, с тем, что вы уже написали. Он находится на правильном пути, но ваш код будет работать только для универсальных типов только с одним универсальным параметром, и он будет работать только тогда, когда сам параметр универсального типа не является универсальным!

Это функция (написанная как метод расширения), которая должна работать во всех случаях:

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 + ">";
    }
}

Эта функция рекурсивна и безопасна. Если вы запустите его на этом входе:

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

Вы получаете этот (правильный) вывод:

Dictionary<String,List<Func<String,Boolean>>>
6 голосов
/ 03 апреля 2014

Хотя принятое решение подходит только для имени или для не вложенного полного имени (путем замены имени на полное имя, как в ответе @Ose E), однако для вложенных типов оно все равно не будет работать, а также не для массивов. универсальных типов.

Итак, вот решение, которое будет работать, (но учтите, что это решение будет устанавливать фактические аргументы только в том случае, если установлены все аргументы, иными словами, даже если декларирующий тип предоставил аргумент типа, пока Внутренний универсальный тип не имеет, он все равно не будет отображаться даже для базы).

    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;
    }
0 голосов
/ 16 августа 2017

Это приведет к тому же результату кода, что и генератор кода CS. Я улучшил код yoel halb.

/// <summary>
    ///     Gets the CS Type Code for a type
    /// </summary>
    /// <param name="type">The type.</param>
    /// <returns></returns>
    /// <exception cref="System.ArgumentNullException">type</exception>
    public static string GetCSTypeName(this Type type)
    {
        if (type == typeof(string))
        {
            return "string";
        }
        else if (type == typeof(object)) { return "object"; }
        else if (type == typeof(bool)) { return "bool"; }
        else if (type == typeof(char)) { return "char"; }
        else if (type == typeof(int)) { return "int"; }
        else if (type == typeof(float)) { return "float"; }
        else if (type == typeof(double)) { return "double"; }
        else if (type == typeof(long)) { return "long"; }
        else if (type == typeof(ulong)) { return "ulong"; }
        else if (type == typeof(uint)) { return "uint"; }
        else if (type == typeof(byte)) { return "byte"; }
        else if (type == typeof(Int64)) { return "Int64"; }
        else if (type == typeof(short)) { return "short"; }
        else if (type == typeof(decimal)) { return "decimal"; }
        else if (type.IsGenericType)
        {
            return $"{ToGenericTypeString(type)}";
        }
        else if (type.IsArray)
        {
            List<string> arrayLength = new List<string>();
            for (int i = 0; i < type.GetArrayRank(); i++)
            {
                arrayLength.Add("[]");
            }
            return GetCSTypeName(type.GetElementType()) + string.Join("", arrayLength).Replace("+", ".");
        }
        else
        {
            return type.FullName.Replace("+", ".");
        }
    }

    private static string ToCSReservatedWord(this Type type, bool fullName)
    {
        if (type == typeof(string))
        {
            return "string";
        }
        else if (type == typeof(object)) { return "object"; }
        else if (type == typeof(bool)) { return "bool"; }
        else if (type == typeof(char)) { return "char"; }
        else if (type == typeof(int)) { return "int"; }
        else if (type == typeof(float)) { return "float"; }
        else if (type == typeof(double)) { return "double"; }
        else if (type == typeof(long)) { return "long"; }
        else if (type == typeof(ulong)) { return "ulong"; }
        else if (type == typeof(uint)) { return "uint"; }
        else if (type == typeof(byte)) { return "byte"; }
        else if (type == typeof(Int64)) { return "Int64"; }
        else if (type == typeof(short)) { return "short"; }
        else if (type == typeof(decimal)) { return "decimal"; }
        else
        {
            if (fullName)
            {
                return type.FullName;
            }
            else
            {
                return type.Name;
            }

        }
    }

    public static string ToGenericTypeString(this Type t, params Type[] arg)
    {
        if (t.IsGenericParameter || t.FullName == null) return t.FullName;//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 ToCSReservatedWord(t, true).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.ToCSReservatedWord(true);
        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) + "." + ToCSReservatedWord(genericType, false);//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.ToCSReservatedWord(true), "").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;
    }

это пройдет, как и ожидалось, следующий модульный тест.

[TestClass]
public class GetCSName
{

    private string GetCSCompilerName(Type type)
    {
        if (type == null)
        {
            throw new ArgumentNullException(nameof(type));
        }
        var compiler = new CSharpCodeProvider();
        var typeRef = new CodeTypeReference(type);
        return compiler.GetTypeOutput(typeRef);
    }

    [TestMethod]
    public void TestMethod1()
    {
        List<Type> typesToTest = new List<Type>();
        typesToTest.Add(typeof(string));
        typesToTest.Add(typeof(string[]));
        typesToTest.Add(typeof(object[]));
        typesToTest.Add(typeof(bool[]));
        typesToTest.Add(typeof(string));
        typesToTest.Add(typeof(object));
        typesToTest.Add(typeof(int));
        typesToTest.Add(typeof(double));
        typesToTest.Add(typeof(float));
        typesToTest.Add(typeof(bool));
        typesToTest.Add(typeof(char));
        typesToTest.Add(typeof(decimal));
        typesToTest.Add(typeof(decimal?[]));
        typesToTest.Add(typeof(decimal?[][]));
        typesToTest.Add(typeof(Int64));
        typesToTest.Add(typeof(Guid));
        typesToTest.Add(typeof(int?));
        typesToTest.Add(typeof(double?));
        typesToTest.Add(typeof(float?));
        typesToTest.Add(typeof(bool?));
        typesToTest.Add(typeof(char?));
        typesToTest.Add(typeof(decimal?));
        typesToTest.Add(typeof(Int64?));
        typesToTest.Add(typeof(Guid?));
        typesToTest.Add(typeof(List<string>));
        typesToTest.Add(typeof(Dictionary<string, Guid>));
        typesToTest.Add(typeof(Dictionary<string, Guid>[]));
        typesToTest.Add(typeof(Dictionary<string, Guid?>));
        typesToTest.Add(typeof(Dictionary<string, Dictionary<string, Guid?>>));
        typesToTest.Add(typeof(Dictionary<string, Dictionary<string, Guid?>>[]));
        typesToTest.Add(typeof(Dictionary<string, Dictionary<string, Guid?>>[][]));
        typesToTest.Add(typeof(int[]));
        typesToTest.Add(typeof(int[][]));
        typesToTest.Add(typeof(int[][][]));
        typesToTest.Add(typeof(int[][][][]));
        typesToTest.Add(typeof(int[][][][][]));
        typesToTest.Add(typeof(TestClass));
        typesToTest.Add(typeof(List<TestClass>));
        typesToTest.Add(typeof(Dictionary<TestClass, TestClass>));
        typesToTest.Add(typeof(Dictionary<string, TestClass>));
        typesToTest.Add(typeof(List<Dictionary<string, TestClass>>));
        typesToTest.Add(typeof(List<Dictionary<string, GenericTestClass<string>>>));
        typesToTest.Add(typeof(GenericTestClass<string>.SecondSubType<decimal>));
        typesToTest.Add(typeof(GenericTestClass<string>.SecondSubType));
        typesToTest.Add(typeof(GenericTestClass<string, int>.SecondSubType));
        typesToTest.Add(typeof(GenericTestClass<string, Dictionary<string,int>>.SecondSubType<string>));
        typesToTest.Add(typeof(GenericTestClass<string, Dictionary<string, int>>.SecondSubType<GenericTestClass<string, Dictionary<string, int>>>));


        foreach (var t in typesToTest)
        {
            if (GetCSCompilerName(t) != t.GetCSTypeName())
            {
                Console.WriteLine($"FullName:\r\n{t.FullName}");
                Console.WriteLine("C " + GetCSCompilerName(t));
                Console.WriteLine("R " + t.GetCSTypeName());
                Console.WriteLine("Equal: " + (GetCSCompilerName(t) == t.GetCSTypeName()));
                Console.WriteLine();

                Assert.Fail($"From CSharpCodeProvider '{GetCSCompilerName(t)}' is not equal to {t.GetCSTypeName()}");
            }
            else
            {
                Console.WriteLine($"Passed: {t.GetCSTypeName()}");
                //ignore because of equal.
            }


        }

    }

    public class TestClass
    {

    }

    public class GenericTestClass<T>
    {
        public class SecondSubType
        {

        }

        public class SecondSubType<T2>
        {

        }
    }

    public class GenericTestClass<T1,T2>
    {
        public class SecondSubType
        {

        }

        public class SecondSubType<T2>
        {

        }
    }
}

Результат будет:

Passed: string
Passed: string[]
Passed: object[]
Passed: bool[]
Passed: string
Passed: object
Passed: int
Passed: double
Passed: float
Passed: bool
Passed: char
Passed: decimal
Passed: System.Nullable<decimal>[]
Passed: System.Nullable<decimal>[][]
Passed: long
Passed: System.Guid
Passed: System.Nullable<int>
Passed: System.Nullable<double>
Passed: System.Nullable<float>
Passed: System.Nullable<bool>
Passed: System.Nullable<char>
Passed: System.Nullable<decimal>
Passed: System.Nullable<long>
Passed: System.Nullable<System.Guid>
Passed: System.Collections.Generic.List<string>
Passed: System.Collections.Generic.Dictionary<string, System.Guid>
Passed: System.Collections.Generic.Dictionary<string, System.Guid>[]
Passed: System.Collections.Generic.Dictionary<string, System.Nullable<System.Guid>>
Passed: System.Collections.Generic.Dictionary<string, System.Collections.Generic.Dictionary<string, System.Nullable<System.Guid>>>
Passed: System.Collections.Generic.Dictionary<string, System.Collections.Generic.Dictionary<string, System.Nullable<System.Guid>>>[]
Passed: System.Collections.Generic.Dictionary<string, System.Collections.Generic.Dictionary<string, System.Nullable<System.Guid>>>[][]
Passed: int[]
Passed: int[][]
Passed: int[][][]
Passed: int[][][][]
Passed: int[][][][][]
Passed: Eneon.Common.Utils.Extensions.Tests.GetCSName.TestClass
Passed: System.Collections.Generic.List<Eneon.Common.Utils.Extensions.Tests.GetCSName.TestClass>
Passed: System.Collections.Generic.Dictionary<Eneon.Common.Utils.Extensions.Tests.GetCSName.TestClass, Eneon.Common.Utils.Extensions.Tests.GetCSName.TestClass>
Passed: System.Collections.Generic.Dictionary<string, Eneon.Common.Utils.Extensions.Tests.GetCSName.TestClass>
Passed: System.Collections.Generic.List<System.Collections.Generic.Dictionary<string, Eneon.Common.Utils.Extensions.Tests.GetCSName.TestClass>>
Passed: System.Collections.Generic.List<System.Collections.Generic.Dictionary<string, Eneon.Common.Utils.Extensions.Tests.GetCSName.GenericTestClass<string>>>
Passed: Eneon.Common.Utils.Extensions.Tests.GetCSName.GenericTestClass<string>.SecondSubType<decimal>
Passed: Eneon.Common.Utils.Extensions.Tests.GetCSName.GenericTestClass<string>.SecondSubType
Passed: Eneon.Common.Utils.Extensions.Tests.GetCSName.GenericTestClass<string, int>.SecondSubType
Passed: Eneon.Common.Utils.Extensions.Tests.GetCSName.GenericTestClass<string, System.Collections.Generic.Dictionary<string, int>>.SecondSubType<string>
Passed: Eneon.Common.Utils.Extensions.Tests.GetCSName.GenericTestClass<string, System.Collections.Generic.Dictionary<string, int>>.SecondSubType<Eneon.Common.Utils.Extensions.Tests.GetCSName.GenericTestClass<string, System.Collections.Generic.Dictionary<string, int>>>
0 голосов
/ 25 февраля 2016

Это мое решение, оно также работает для вложенных классов и обобщений:

    public static string GenericTypeString(this Type t)
    {
        if (!t.IsGenericType)
        {
            return t.GetFullNameWithoutNamespace()
                    .ReplacePlusWithDotInNestedTypeName();
        }

        return t.GetGenericTypeDefinition()
                .GetFullNameWithoutNamespace()
                .ReplacePlusWithDotInNestedTypeName()
                .ReplaceGenericParametersInGenericTypeName(t);
    }

    private static string GetFullNameWithoutNamespace(this Type type)
    {
        if (type.IsGenericParameter)
        {
            return type.Name;
        }

        const int dotLength = 1;
        return type.FullName.Substring(type.Namespace.Length + dotLength);
    }

    private static string ReplacePlusWithDotInNestedTypeName(this string typeName)
    {
        return typeName.Replace('+', '.');
    }

    private static string ReplaceGenericParametersInGenericTypeName(this string typeName, Type t)
    {
        var genericArguments = t.GetGenericArguments();

        const string regexForGenericArguments = @"`[1-9]\d*";

        var rgx = new Regex(regexForGenericArguments);

        typeName = rgx.Replace(typeName, match =>
        {
            var currentGenericArgumentNumbers = int.Parse(match.Value.Substring(1));
            var currentArguments = string.Join(",", genericArguments.Take(currentGenericArgumentNumbers).Select(GenericTypeString));
            genericArguments = genericArguments.Skip(currentGenericArgumentNumbers).ToArray();
            return string.Concat("<", currentArguments, ">");
        });

        return typeName;
    }
0 голосов
/ 12 февраля 2014

Незначительное добавление к @ 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 + ">";
}
...