किसी भिन्न असेंबली में वर्ग नाम से प्रकार हल करें


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

बहुत बढ़िया, मुझे पता था कि मुझे विधानसभा सहित कुछ मामूली याद आ रही थी। इस समाधान ने मेरी जरूरतों के लिए काम किया। धन्यवाद।
ब्रैंडन

11
और क्रमांकन में काम करने वालों के लिए: असेंबली-योग्य नाम प्राप्त करने के लिए, संपत्ति टाइप
माइकल वाइल्ड

1
यदि प्रकार एक सूची <टी> है, जहां टी एक कस्टम वर्ग है, तो आप 2 विधानसभाओं को कैसे निर्दिष्ट करते हैं? यानी। Mscorlib विधानसभा के लिए System.Collections.Generic.List, और पुस्तकालय जिसमें T शामिल है?
साइमन ग्रीन

@SimonGreen: आपको संभवतः इसका उपयोग करके निर्माण करना होगा listType.MakeGenericType(itemType)Type.GetType()मेरे जवाब की तरह दोनों प्रकार के चर का निर्माण किया जा सकता है ।
Sandor Drieënhuizen

object.Assembly.ToString () का उपयोग पूर्ण असेंबली प्राप्त करने के लिए भी किया जा सकता है।
zezba9000

7

यह सार्वभौमिक समाधान उन लोगों के लिए है जिन्हें गतिशील बाहरी संदर्भों से जेनेरिक प्रकारों को लोड करने की आवश्यकता है 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

ओपी के समान, मुझे नाम से प्रकारों के एक सीमित उपसमूह को लोड करने की आवश्यकता थी (मेरे मामले में सभी कक्षाएं एक ही विधानसभा में थीं और एक ही इंटरफ़ेस लागू किया गया था)। जब मैं Type.GetType(string)एक अलग विधानसभा के खिलाफ उपयोग करने की कोशिश कर रहा था, तब भी बहुत सारे अजीब मुद्दे थे (यहां तक ​​कि अन्य पदों में उल्लिखित असेंबलीक्यूलाइज्डनाम को जोड़कर)। यहाँ मैं इस मुद्दे को हल कैसे है:

उपयोग:

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

जाहिर है आप AppDomain, या अन्य तर्क है कि बेहतर अपने उपयोग के मामले में फिट बैठता है में सभी विधानसभाओं का निरीक्षण करने के लिए CacheTypes विधि tweak कर सकता है। यदि आपका उपयोग-मामला कई नामस्थानों से लोड किए जाने के प्रकारों की अनुमति देता है, तो आप टाइप के FullNameबजाय इसका उपयोग करने के लिए शब्दकोश कुंजी को बदलना चाह सकते हैं । या यदि आपके प्रकार एक सामान्य इंटरफ़ेस या बेस क्लास से विरासत में नहीं आते हैं, तो आप <BaseType>कुछ चीज़ों का उपयोग करने के लिए CacheTypes विधि को हटा और बदल सकते हैं.GetTypes().Where(t => t.Namespace.StartsWith("MyProject.Domain.Model.")


1

पहले विधानसभा को लोड करें और फिर टाइप करें। उदा: असेंबली DLL = असेंबली। लोडेड (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 के साथ एक पकड़ने की कोशिश ब्लॉक में लपेट सकते हैं। इसमें बहुत अधिक गलत होने की संभावना है - "MyProject.Domain.Model.ClassName, ClassName, Version = 2.0.0.0, संस्कृति = तटस्थ, PublicKeyToken = b77a5c56193498989" तो इसमें - "MyProject.Domain.Model।" ...
सिमोनबोर

1
@ मुझे यकीन नहीं है कि सिमोनबोर का क्या मतलब है, लेकिन यदि आप स्ट्रिंग का उपयोग करते समय GetType ()। असेंबली क्वॉलिफाइंडनेम का उपयोग करते हैं, तो आपको स्ट्रिंग का उपयोग करते समय इसके बारे में चिंता करने की ज़रूरत नहीं है।
सर्जियो पोर्र्स
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.