다른 어셈블리의 클래스 이름에서 유형 확인


87

클래스 유형을 해결해야하는 방법이 있습니다. 이 클래스는 다음과 유사한 네임 스페이스를 가진 다른 어셈블리에 있습니다.

MyProject.Domain.Model

다음을 수행하려고합니다.

Type.GetType("MyProject.Domain.Model." + myClassName);

이 작업을 수행하는 코드가 확인하려는 유형의 클래스와 동일한 어셈블리에있는 경우 잘 작동하지만 클래스가 다른 어셈블리에 있으면이 코드가 실패합니다.

이 작업을 수행하는 훨씬 더 나은 방법이 있다고 확신하지만, 내가 찾고있는 클래스 유형을 해결하기 위해 어셈블리를 해결하고 네임 스페이스를 탐색하는 데 많은 경험이 없습니다. 이 작업을보다 우아하게 수행하기위한 조언이나 팁이 있습니까?


답변:


171

다음과 같이 어셈블리 이름을 추가해야합니다.

Type.GetType("MyProject.Domain.Model." + myClassName + ", AssemblyName");

모호함을 방지하거나 어셈블리가 GAC에있는 경우 다음과 같은 정규화 된 어셈블리 이름을 제공해야합니다.

Type.GetType("System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");

훌륭합니다. 어셈블리를 포함하여 사소한 것을 놓치고 있다는 것을 알았습니다. 이 솔루션은 내 요구에 적합했습니다. 감사.
Brandon

10
그리고 연재에서 다루는 사람들을 위해 : 어셈블리 자격을 갖춘 이름을 얻으려면, 속성이 Type.AssemblyQualifiedName은
마이클 와일드

1
형식이 List <T>이고 여기서 T는 사용자 지정 클래스 인 경우 어셈블리 2 개를 어떻게 지정합니까? 즉 .System.Collections.Generic.List에 대한 mscorlib 어셈블리 및 T?
Simon Green

@SimonGreen : 아마도 listType.MakeGenericType(itemType). 두 유형 변수 모두 Type.GetType()내 대답에서 like를 사용하여 구성 할 수 있습니다 .
Sandor Drieënhuizen

object.Assembly.ToString () 전체 어셈블리를 가져 오는데도 사용할 수 있습니다.
zezba9000

5

이 보편적 인 솔루션은 로드 필요로하는 사람을위한 동적 외부 참조에서 제네릭 형식 에 의해을 AssemblyQualifiedName있는 조립 제네릭 형식의 모든 부분에서 오는 모르게 :

    public static Type ReconstructType(string assemblyQualifiedName, bool throwOnError = true, params Assembly[] referencedAssemblies)
    {
        foreach (Assembly asm in referencedAssemblies)
        {
            var fullNameWithoutAssemblyName = assemblyQualifiedName.Replace($", {asm.FullName}", "");
            var type = asm.GetType(fullNameWithoutAssemblyName, throwOnError: false);
            if (type != null) return type;
        }

        if (assemblyQualifiedName.Contains("[["))
        {
            Type type = ConstructGenericType(assemblyQualifiedName, throwOnError);
            if (type != null)
                return type;
        }
        else
        {
            Type type = Type.GetType(assemblyQualifiedName, false);
            if (type != null)
                return type;
        }

        if (throwOnError)
            throw new Exception($"The type \"{assemblyQualifiedName}\" cannot be found in referenced assemblies.");
        else
            return null;
    }

    private static Type ConstructGenericType(string assemblyQualifiedName, bool throwOnError = true)
    {
        Regex regex = new Regex(@"^(?<name>\w+(\.\w+)*)`(?<count>\d)\[(?<subtypes>\[.*\])\](, (?<assembly>\w+(\.\w+)*)[\w\s,=\.]+)$?", RegexOptions.Singleline | RegexOptions.ExplicitCapture);
        Match match = regex.Match(assemblyQualifiedName);
        if (!match.Success)
            if (!throwOnError) return null;
            else throw new Exception($"Unable to parse the type's assembly qualified name: {assemblyQualifiedName}");

        string typeName = match.Groups["name"].Value;
        int n = int.Parse(match.Groups["count"].Value);
        string asmName = match.Groups["assembly"].Value;
        string subtypes = match.Groups["subtypes"].Value;

        typeName = typeName + $"`{n}";
        Type genericType = ReconstructType(typeName, throwOnError);
        if (genericType == null) return null;

        List<string> typeNames = new List<string>();
        int ofs = 0;
        while (ofs < subtypes.Length && subtypes[ofs] == '[')
        {
            int end = ofs, level = 0;
            do
            {
                switch (subtypes[end++])
                {
                    case '[': level++; break;
                    case ']': level--; break;
                }
            } while (level > 0 && end < subtypes.Length);

            if (level == 0)
            {
                typeNames.Add(subtypes.Substring(ofs + 1, end - ofs - 2));
                if (end < subtypes.Length && subtypes[end] == ',')
                    end++;
            }

            ofs = end;
            n--;  // just for checking the count
        }

        if (n != 0)
            // This shouldn't ever happen!
            throw new Exception("Generic type argument count mismatch! Type name: " + assemblyQualifiedName);  

        Type[] types = new Type[typeNames.Count];
        for (int i = 0; i < types.Length; i++)
        {
            try
            {
                types[i] = ReconstructType(typeNames[i], throwOnError);
                if (types[i] == null)  // if throwOnError, should not reach this point if couldn't create the type
                    return null;
            }
            catch (Exception ex)
            {
                throw new Exception($"Unable to reconstruct generic type. Failed on creating the type argument {(i + 1)}: {typeNames[i]}. Error message: {ex.Message}");
            }
        }

        Type resultType = genericType.MakeGenericType(types);
        return resultType;
    }

그리고 당신은이 코드를 테스트 할 수 있습니다 (콘솔 응용 프로그램) :

    static void Main(string[] args)
    {
        Type t1 = typeof(Task<Dictionary<int, Dictionary<string, int?>>>);
        string name = t1.AssemblyQualifiedName;
        Console.WriteLine("Type: " + name);
        // Result: System.Threading.Tasks.Task`1[[System.Collections.Generic.Dictionary`2[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Collections.Generic.Dictionary`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Nullable`1[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
        Type t2 = ReconstructType(name);
        bool ok = t1 == t2;
        Console.WriteLine("\r\nLocal type test OK: " + ok);

        Assembly asmRef = Assembly.ReflectionOnlyLoad("System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
        // Task<DialogResult> in refTypeTest below:
        string refTypeTest = "System.Threading.Tasks.Task`1[[System.Windows.Forms.DialogResult, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089";
        Type t3 = ReconstructType(refTypeTest, true, asmRef);
        Console.WriteLine("External type test OK: " + (t3.AssemblyQualifiedName == refTypeTest));

        // Getting an external non-generic type directly from references:
        Type t4 = ReconstructType("System.Windows.Forms.DialogResult, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", true, asmRef);

        Console.ReadLine();
    }

저와 동일한 문제를 가진 사람들을 돕기 위해 솔루션을 공유하고 있습니다 (외부 참조 어셈블리에서 부분적으로 또는 전체적으로 정의 될 수있는 문자열에서 모든 유형을 역 직렬화하고 참조는 앱 사용자가 동적으로 추가 함).

누구에게나 도움이되기를 바랍니다!


2

OP와 유사하게 이름별로 제한된 유형의 하위 집합을로드해야했습니다 (제 경우에는 모든 클래스가 단일 어셈블리에 있고 동일한 인터페이스를 구현했습니다). Type.GetType(string)다른 어셈블리에 대해 사용하려고 할 때 이상한 문제가 많이 발생 했습니다 (다른 게시물에서 언급 한대로 AssemblyQualifiedName을 추가하는 경우도 있음). 문제를 해결 한 방법은 다음과 같습니다.

용법:

var mytype = TypeConverter<ICommand>.FromString("CreateCustomer");

암호:

    public class TypeConverter<BaseType>
        {
            private static Dictionary<string, Type> _types;
            private static object _lock = new object();

            public static Type FromString(string typeName)
            {
                if (_types == null) CacheTypes();

                if (_types.ContainsKey(typeName))
                {
                    return _types[typeName];
                }
                else
                {
                    return null;
                }
            }

            private static void CacheTypes()
            {
                lock (_lock)
                {
                    if (_types == null)
                    {
                        // Initialize the myTypes list.
                        var baseType = typeof(BaseType);
                        var typeAssembly = baseType.Assembly;
                        var types = typeAssembly.GetTypes().Where(t => 
                            t.IsClass && 
                            !t.IsAbstract && 
                            baseType.IsAssignableFrom(t));

                        _types = types.ToDictionary(t => t.Name);
                    }
                }
            }
        }

분명히 CacheTypes 메서드를 조정하여 AppDomain의 모든 어셈블리 또는 사용 사례에 더 잘 맞는 다른 논리를 검사 할 수 있습니다. 사용 사례가 여러 네임 스페이스에서 형식을로드 할 수 있도록 허용하는 경우 해당 형식을 FullName대신 사용하도록 사전 키를 변경할 수 있습니다 . 또는 유형이 공통 인터페이스 또는 기본 클래스에서 상속되지 않는 <BaseType>경우 다음과 같이 사용하도록 CacheTypes 메서드를 제거 하고 변경할 수 있습니다..GetTypes().Where(t => t.Namespace.StartsWith("MyProject.Domain.Model.")


1

먼저 어셈블리를로드 한 다음 유형을로드합니다. 예 : 어셈블리 DLL = Assembly.LoadFile (PATH); DLL.GetType (typeName);


0

표준 방법 중 하나를 사용할 수 있습니까?

typeof( MyClass );

MyClass c = new MyClass();
c.GetType();

그렇지 않은 경우 어셈블리에 대한 정보를 Type.GetType에 추가해야합니다.


0

AssemblyQualifiedName속성을 이용한 짧고 역동적 인 접근 -

Type.GetType(Type.GetType("MyProject.Domain.Model." + myClassName).AssemblyQualifiedName)

즐겨!


10
Type.GetType ( "MyProject.Domain.Model."+ myClassName)이 실패하면 어떻게 다른 GetType 호출로 래핑하여이를 방지 할 수 있습니까?
Kaine

1
어쨌든 System.NullReferenceException을 사용하여 try catch 블록으로 래핑 할 수 있습니다. "MyProject.Domain.Model.ClassName, ClassName, Version = 2.0.0.0, Culture = neutral, PublicKeyToken = b77a5c561934e089"에서 착각 할 가능성이 훨씬 더 높고 "MyProject.Domain.Model."...
simonbor

1
@Kaine simonbor가 무엇을 의미하는지 잘 모르겠지만 문자열을 쓸 때 GetType (). AssemblyQualifiedName을 사용하면 문자열을 사용하여 유형을 확인할 때 걱정할 필요가 없습니다.
Sergio Porres 19
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.