سؤال

باستخدام الانعكاس، كيف يمكنني الحصول على جميع الأنواع التي تنفذ واجهة مع C# 3.0/.NET 3.5 بأقل قدر من التعليمات البرمجية وتقليل التكرارات؟

وهذا ما أريد إعادة كتابته:

foreach (Type t in this.GetType().Assembly.GetTypes())
    if (t is IMyInterface)
        ; //do stuff
هل كانت مفيدة؟

المحلول

سيكون هذا في c# 3.0 :)

var type = typeof(IMyInterface);
var types = AppDomain.CurrentDomain.GetAssemblies()
    .SelectMany(s => s.GetTypes())
    .Where(p => type.IsAssignableFrom(p));

بشكل أساسي، أقل قدر من التكرارات سيكون دائمًا:

loop assemblies  
 loop types  
  see if implemented.

نصائح أخرى

لقد نجح هذا بالنسبة لي.فهو يتكرر عبر الفئات ويتحقق لمعرفة ما إذا كانت مشتقة من myInterface

 foreach (Type mytype in System.Reflection.Assembly.GetExecutingAssembly().GetTypes()
                 .Where(mytype => mytype .GetInterfaces().Contains(typeof(myInterface)))) {
    //do stuff
 }

للعثور على كافة الأنواع في التجميع الذي يقوم بتنفيذ واجهة IFoo:

var results = from type in someAssembly.GetTypes()
              where typeof(IFoo).IsAssignableFrom(type)
              select type;

لاحظ أن اقتراح ريان رينالدي كان غير صحيح.وسوف يعود 0 أنواع.لا يمكنك الكتابة

where type is IFoo

لأن النوع هو مثيل System.Type، ولن يكون من النوع IFoo أبدًا.بدلاً من ذلك، عليك التحقق لمعرفة ما إذا كان IFoo قابلاً للتخصيص من النوع.وهذا سوف تحصل على النتائج المتوقعة.

كما أن اقتراح آدم رايت، الذي تم وضع علامة عليه حاليًا كإجابة، غير صحيح أيضًا، ولنفس السبب.في وقت التشغيل، سترى 0 أنواع تعود، لأن جميع مثيلات System.Type لم تكن تطبيقات IFoo.

أقدر أن هذا سؤال قديم جدًا ولكني اعتقدت أنني سأضيف إجابة أخرى للمستخدمين المستقبليين حيث أن جميع الإجابات حتى الآن تستخدم شكلاً من أشكال Assembly.GetTypes.

على الرغم من أن GetTypes()‎ ستعيد جميع الأنواع بالفعل، إلا أن هذا لا يعني بالضرورة أنه يمكنك تنشيطها وبالتالي من المحتمل أن تؤدي إلى ReflectionTypeLoadException.

المثال الكلاسيكي لعدم القدرة على تنشيط النوع هو عندما يكون النوع الذي تم إرجاعه derived من base لكن base يتم تعريفه في تجميع مختلف عن ذلك الخاص بـ derived, ، تجميع لا يشير إليه التجميع المستدعي.

لذلك نقول لدينا:

Class A // in AssemblyA
Class B : Class A, IMyInterface // in AssemblyB
Class C // in AssemblyC which references AssemblyB but not AssemblyA

إذا كان في ClassC وهو في AssemblyC ثم نقوم بشيء حسب الإجابة المقبولة:

var type = typeof(IMyInterface);
var types = AppDomain.CurrentDomain.GetAssemblies()
    .SelectMany(s => s.GetTypes())
    .Where(p => type.IsAssignableFrom(p));

ثم سوف يرمي ReflectionTypeLoadException.

وذلك لأنه دون الإشارة إلى AssemblyA في AssemblyC لن تكون قادرًا على:

var bType = typeof(ClassB);
var bClass = (ClassB)Activator.CreateInstance(bType);

بعبارة أخرى ClassB ليس قابل للتحميل وهو الأمر الذي يتم فحصه واستدعاء GetTypes.

لذلك، لتأهيل مجموعة النتائج بأمان للأنواع القابلة للتحميل، وفقًا لذلك فيل هاكد شرط احصل على جميع الأنواع في مجموعة واحدة و كود جون سكيت ستفعل بدلاً من ذلك شيئًا مثل:

public static class TypeLoaderExtensions {
    public static IEnumerable<Type> GetLoadableTypes(this Assembly assembly) {
        if (assembly == null) throw new ArgumentNullException("assembly");
        try {
            return assembly.GetTypes();
        } catch (ReflectionTypeLoadException e) {
            return e.Types.Where(t => t != null);
        }
    }
}

وثم:

private IEnumerable<Type> GetTypesWithInterface(Assembly asm) {
    var it = typeof (IMyInterface);
    return asm.GetLoadableTypes().Where(it.IsAssignableFrom).ToList();
}

الإجابات الأخرى هنا تستخدم IsAssignableFrom.تستطيع ايضا استخذام FindInterfaces من System مساحة الاسم، كما هو موضح هنا.

فيما يلي مثال يتحقق من كافة التجميعات الموجودة في مجلد التجميع الذي يتم تنفيذه حاليًا، ويبحث عن الفئات التي تنفذ واجهة معينة (تجنب LINQ من أجل الوضوح).

static void Main() {
    const string qualifiedInterfaceName = "Interfaces.IMyInterface";
    var interfaceFilter = new TypeFilter(InterfaceFilter);
    var path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
    var di = new DirectoryInfo(path);
    foreach (var file in di.GetFiles("*.dll")) {
        try {
            var nextAssembly = Assembly.ReflectionOnlyLoadFrom(file.FullName);
            foreach (var type in nextAssembly.GetTypes()) {
                var myInterfaces = type.FindInterfaces(interfaceFilter, qualifiedInterfaceName);
                if (myInterfaces.Length > 0) {
                    // This class implements the interface
                }
            }
        } catch (BadImageFormatException) {
            // Not a .net assembly  - ignore
        }
    }
}

public static bool InterfaceFilter(Type typeObj, Object criteriaObj) {
    return typeObj.ToString() == criteriaObj.ToString();
}

يمكنك إعداد قائمة بالواجهات إذا كنت تريد مطابقة أكثر من واجهة واحدة.

قم بالتكرار عبر كافة التجميعات المحملة، والتكرار عبر جميع أنواعها، والتحقق من أنها تقوم بتنفيذ الواجهة.

شيء مثل:

Type ti = typeof(IYourInterface);
foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies()) {
    foreach (Type t in asm.GetTypes()) {
        if (ti.IsAssignableFrom(t)) {
            // here's your type in t
        }
    }
}

لقد نجح هذا بالنسبة لي (إذا كنت ترغب في ذلك، يمكنك استبعاد أنواع النظام في البحث):

Type lookupType = typeof (IMenuItem);
IEnumerable<Type> lookupTypes = GetType().Assembly.GetTypes().Where(
        t => lookupType.IsAssignableFrom(t) && !t.IsInterface); 

يحرر:لقد رأيت للتو التعديل لتوضيح أن السؤال الأصلي كان يتعلق بتقليل التكرارات/الكود وهذا أمر جيد وجيد كتمرين، ولكن في مواقف العالم الحقيقي، ستحتاج إلى أسرع تنفيذ، بغض النظر عن الكيفية. تبريد مظهر LINQ الأساسي.

هذه هي طريقة Utils الخاصة بي للتكرار عبر الأنواع المحملة.إنه يتعامل مع الفئات العادية بالإضافة إلى الواجهات، ويعمل خيار ExceptySystemTypes على تسريع الأمور بشكل كبير إذا كنت تبحث عن تطبيقات في قاعدة التعليمات البرمجية الخاصة بك/لجهة خارجية.

public static List<Type> GetSubclassesOf(this Type type, bool excludeSystemTypes) {
    List<Type> list = new List<Type>();
    IEnumerator enumerator = Thread.GetDomain().GetAssemblies().GetEnumerator();
    while (enumerator.MoveNext()) {
        try {
            Type[] types = ((Assembly) enumerator.Current).GetTypes();
            if (!excludeSystemTypes || (excludeSystemTypes && !((Assembly) enumerator.Current).FullName.StartsWith("System."))) {
                IEnumerator enumerator2 = types.GetEnumerator();
                while (enumerator2.MoveNext()) {
                    Type current = (Type) enumerator2.Current;
                    if (type.IsInterface) {
                        if (current.GetInterface(type.FullName) != null) {
                            list.Add(current);
                        }
                    } else if (current.IsSubclassOf(type)) {
                        list.Add(current);
                    }
                }
            }
        } catch {
        }
    }
    return list;
}

إنها ليست جميلة، سأعترف بذلك.

الإجابات الأخرى لم تكن تعمل مع أ واجهة عامة.

هذا ما يفعله، فقط استبدل typeof(ISomeInterface) بنوع (T).

List<string> types = AppDomain.CurrentDomain.GetAssemblies().SelectMany(x => x.GetTypes())
            .Where(x => typeof(ISomeInterface).IsAssignableFrom(x) && !x.IsInterface && !x.IsAbstract)
            .Select(x => x.Name).ToList();

حتى مع

AppDomain.CurrentDomain.GetAssemblies().SelectMany(x => x.GetTypes())

نحصل على جميع الجمعيات

!x.IsInterface && !x.IsAbstract

يتم استخدامه لاستبعاد الواجهة والأخرى المجردة و

.Select(x => x.Name).ToList();

ليكون لهم في القائمة.

لا توجد طريقة سهلة (من حيث الأداء) لفعل ما تريد القيام به.

يعمل الانعكاس مع التجميعات والأنواع بشكل أساسي، لذا سيتعين عليك الحصول على جميع أنواع التجميعات والاستعلام عنها للحصول على الواجهة الصحيحة.هنا مثال:

Assembly asm = Assembly.Load("MyAssembly");
Type[] types = asm.GetTypes();
Type[] result = types.where(x => x.GetInterface("IMyInterface") != null);

سيؤدي ذلك إلى حصولك على جميع الأنواع التي تطبق IMyInterface في Assembly MyAssembly

والأفضل من ذلك عند اختيار موقع التجميع.قم بتصفية معظم التجميعات إذا كنت تعرف أن جميع الواجهات المطبقة موجودة ضمن نفس Assembly.DefinedTypes.

// We get the assembly through the base class
var baseAssembly = typeof(baseClass).GetTypeInfo().Assembly;

// we filter the defined classes according to the interfaces they implement
var typeList = baseAssembly.DefinedTypes.Where(type => type.ImplementedInterfaces.Any(inter => inter == typeof(IMyInterface))).ToList();

بقلم كان بيلجين

لقد حصلت على استثناءات في كود linq لذا أفعل ذلك بهذه الطريقة (بدون امتداد معقد):

private static IList<Type> loadAllTypes(Types[] interfaces)
{
    IList<Type> objects = new List<Type>();

    // find all types
    foreach (var interfaceType in interfaces)
        foreach (var currentAsm in AppDomain.CurrentDomain.GetAssemblies())
            try
            {
                foreach (var currentType in currentAsm.GetTypes())
                    if (interfaceType.IsAssignableFrom(currentType) && currentType.IsClass && !currentType.IsAbstract)
                        objects.Add(currentType);
            }
            catch { }

    return objects;
}

يمكنك استخدام بعض LINQ للحصول على القائمة:

var types = from type in this.GetType().Assembly.GetTypes()
            where type is ISomeInterface
            select type;

ولكن في الحقيقة، هل هذا أكثر قابلية للقراءة؟

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top