Türü Farklı Bir Montajdaki Sınıf Adından Çözümle


87

Bir sınıfın Tipini çözmem gereken bir yöntemim var. Bu sınıf, aşağıdakine benzer ad alanına sahip başka bir derlemede bulunur:

MyProject.Domain.Model

Aşağıdakileri gerçekleştirmeye çalışıyorum:

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

Bu eylemi gerçekleştiren kod, türünü çözmeye çalıştığım sınıfla aynı derlemede ise bu harika çalışıyor, ancak sınıfım farklı bir derlemede ise bu kod başarısız oluyor.

Bu görevi yerine getirmenin çok daha iyi bir yolu olduğundan eminim, ancak aradığım sınıfın türünü çözmek için derlemeleri çözme ve içindeki ad alanlarını geçme konusunda çok fazla deneyimim olmadı. Bu görevi daha zarif bir şekilde gerçekleştirmek için herhangi bir tavsiye veya ipucu var mı?


Yanıtlar:


171

Montaj adını şu şekilde eklemeniz gerekecek:

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

Belirsizliği önlemek için veya derleme GAC'de bulunuyorsa, aşağıdaki gibi tam nitelikli bir derleme adı sağlamalısınız:

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

Mükemmel, montaj dahil küçük bir şeyi kaçırdığımı biliyordum. Bu çözüm ihtiyaçlarım için çalıştı. Teşekkürler.
Brandon

11
Ve serileştirmeyle ilgilenenler için: Derleme nitelikli adı elde etmek için, Type.AssemblyQualifiedName
Michael Wild

1
Tür bir List <T> ise, burada T özel bir sınıfsa, 2 derlemeyi nasıl belirtirsiniz? Yani, System.Collections.Generic.List için mscorlib derlemesi ve T? İçeren kitaplık?
Simon Green

@SimonGreen: Muhtemelen onu kullanarak inşa etmeniz gerekecek listType.MakeGenericType(itemType). Her iki tür değişkeni de cevabımdaki Type.GetType()gibi yapılandırılabilir .
Sandor Drieënhuizen

object.Assembly.ToString () Tam montajı almak için de kullanılabilir.
zezba9000

7

Bu evrensel bir çözüm yüklemek için gereken insanlar içindir dinamik dış referanslardan jenerik türlerini tarafından AssemblyQualifiedNamehangi montaj jenerik türdeki tüm parçaları geldiğini bilmeden,:

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

Ve bu kodla test edebilirsiniz (konsol uygulaması):

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

Benimle aynı sorunu yaşayan insanlara yardımcı olmak için çözümümü paylaşıyorum (harici referanslı derlemede hem kısmen hem de bütün olarak tanımlanabilen dizeden HERHANGİ bir türü seriyi kaldırmak için - ve referanslar uygulamanın kullanıcısı tarafından dinamik olarak eklenir).

Umarım herkese yardımcı olur!


2

OP'ye benzer şekilde, sınırlı bir tür alt kümesini ada göre yüklemem gerekiyordu (benim durumumda tüm sınıflar tek bir derlemedeydi ve aynı arabirimi uyguladı). Type.GetType(string)Farklı bir derlemeye karşı kullanmaya çalışırken birçok tuhaf sorun yaşadım (hatta diğer yayınlarda belirtildiği gibi AssemblyQualifiedName'i ekledim). Sorunu şu şekilde çözdüm:

Kullanım:

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

Kod:

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

Açıkçası, AppDomain'deki tüm derlemeleri veya kullanım durumunuza daha iyi uyan diğer mantığı incelemek için CacheTypes yöntemini ince ayar yapabilirsiniz. Kullanım durumunuz türlerin birden çok ad alanından yüklenmesine izin veriyorsa, FullNamebunun yerine türünün kullanılması için sözlük anahtarını değiştirmek isteyebilirsiniz . Veya türleriniz ortak bir arayüzden veya temel sınıftan miras <BaseType>almıyorsa, CacheTypes yöntemini kaldırabilir ve aşağıdaki gibi bir şey kullanacak şekilde değiştirebilirsiniz..GetTypes().Where(t => t.Namespace.StartsWith("MyProject.Domain.Model.")


1

Önce montajı ve ardından tipi yükleyin. örnek: Assembly DLL = Assembly.LoadFile (PATH); DLL.GetType (typeName);


0

Standart yöntemlerden birini kullanabilir misiniz?

typeof( MyClass );

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

Değilse, montaj hakkında Type.GetType'a bilgi eklemeniz gerekecektir.


0

AssemblyQualifiedNameMülkiyet kullanarak kısa ve dinamik yaklaşım -

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

Zevk almak!


10
Type.GetType ("MyProject.Domain.Model." + MyClassName) başarısız olursa, başka bir GetType çağrısına sarmak bunu nasıl önleyebilir?
Kaine

1
Her durumda, onu bir System.NullReferenceException ile bir deneme yakalama bloğuna sarabilirsiniz. Bunda yanılma olasılığı çok daha yüksektir - "MyProject.Domain.Model.ClassName, ClassName, Version = 2.0.0.0, Culture = nötr, PublicKeyToken = b77a5c561934e089" ve sonra bu - "MyProject.Domain.Model." ...
simonbor

1
@Kaine Simonbor'un ne anlama geldiğinden emin değilim, ancak GetType () kullanırsanız AssemblyQualifiedName dizeyi YAZIRKEN, bir türe çözümlemek için dizeyi kullanırken bunun için endişelenmenize gerek yoktur.
Sergio Porres
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.