كيف يمكنني إجبار جميع الفئات المشتقة على تنفيذ طريقة أو خاصية مجردة؟

StackOverflow https://stackoverflow.com/questions/1137677

  •  16-09-2019
  •  | 
  •  

سؤال

يجب تنفيذ وظيفة مجردة من قبل جميع الفئات المحددة.

في بعض الأحيان تريد إجبار جميع الفئات المشتقة على تنفيذ الوظيفة المجردة، حتى مشتقات الفئات المحددة.

class Base { protected abstract Base Clone(); }
class Concrete : Base { protected override Base Clone(){...}; }
class Custom : Concrete {}

أود أن يخبر المترجم المبرمج أن الفصل Custom يحتاج إلى التنفيذ Clone().هل هناك طريقة؟

هل كانت مفيدة؟

المحلول

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

نصائح أخرى

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

إذا لم يكن لديك أي وظيفة في طريقة Concrete.Clone()، فيمكنك إنشاء ملخص فئة "Concrete" الخاصة بك أيضًا (فقط تأكد من تغيير الاسم ;-).اترك أي مرجع لطريقة Clone().

abstract class Base { protected abstract void Clone(); }
abstract class Concrete : Base { }
class Custom : Concrete { protected override void Clone() { /* do something */ } }

إذا كانت لديك بعض الوظائف الأساسية في طريقة Concrete.Clone()، ولكنك تحتاج إلى معلومات تفصيلية من مستوى أعلى، فقم بتقسيمها إلى طريقة مجردة خاصة بها أو خاصية تجبر التنفيذ على مستوى أعلى على توفير هذه المعلومات.

abstract class Base { protected abstract void Clone(); }

abstract class ConcreteForDatabases : Base 
{ 
    protected abstract string CopyInsertStatemement {get;}

    protected override void Clone()
    {
        // setup db connection & command objects
        string sql = CopyInsertStatemement;
        // process the statement
        // clean up db objects
    }
}

class CustomBusinessThingy : ConcreteForDatabases 
{
    protected override string CopyInsertStatemement {get{return "insert myTable(...) select ... from myTable where ...";}}
}

سيكون عليك القيام بذلك Concrete فئة مجردة لفرض ذلك.

يمكنك التحقق من ذلك في وقت التشغيل باستخدام الانعكاس وطرح استثناء لتعطيل التنفيذ، مما يؤدي إلى تدمير المستخدمين "غير المهذبين" لمكتبتك.من ناحية الأداء، هذا ليس حكيمًا جدًا، على الرغم من أنه يمكنك تخزين ثابت هاشسيت<System.Type> في فئة الملخص الأساسية مع جميع الأنواع التي تم التحقق منها.

أعتقد أن أفضل رهان لك هو تقديم وثائق واضحة تخبر أي مستخدم للتعليمات البرمجية الخاصة بك أنه من الضروري تجاوز طريقة Clone().

لقد قمت بإجراء اختبار NUnit التالي الذي يستخدم الانعكاس للتحقق من التنفيذ.نأمل أن تتمكن من التكيف حسب الحاجة.

أظن أنه لن يتعامل مع الأساليب المثقلة بشكل جيد، لكنه يكفي لما أريد.

(التعليقات موضع ترحيب)

/// <summary>
/// Use on a (possibly abstract) method or property to indicate that all subclasses must provide their own implementation.
/// 
/// This is stronger than just abstract, as when you have
/// 
/// A { public abstract void Method()}
/// B: A { public override void Method(){} }
/// C: B {} 
/// 
/// C will be marked as an error
/// </summary>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)]
public class AllSubclassesMustOverrideAttribute : Attribute
{

}

[TestFixture]
public class AllSubclassesMustOverrideAttributeTest
{
    [Test]
    public void SubclassesOverride()
    {
        var failingClasses = new List<string>();

        foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
        {
            try
            {
                foreach (var type in assembly.GetTypes())
                {
                    foreach (var methodInfo in type.GetMethods().Where(m => m.HasAttributeOfType<AllSubclassesMustOverrideAttribute>()))
                    {
                        foreach (var subClass in type.ThisTypeAndSubClasses())
                        {
                            var subclassMethod = subClass.GetMethod(methodInfo.Name);

                            if (subclassMethod.DeclaringType != subClass)
                            {
                                failingClasses.Add(string.Format("Class {0} has no override for method {1}", subClass.FullName, methodInfo.Name));
                            }
                        }
                    }

                    foreach (var propertyInfo in type.GetProperties().Where(p => p.HasAttributeOfType<AllSubclassesMustOverrideAttribute>()))
                    {
                        foreach (var subClass in type.ThisTypeAndSubClasses())
                        {
                            var subclassProperty = subClass.GetProperty(propertyInfo.Name);

                            if (subclassProperty.DeclaringType != subClass)
                            {
                                failingClasses.Add(string.Format("Class {0} has no override for property {1}", subClass.FullName, propertyInfo.Name));
                            }
                        }

                    }
                }
            }
            catch (ReflectionTypeLoadException)
            {
                // This will happen sometimes when running the tests in the NUnit runner. Ignore.
            }
        }

        if (failingClasses.Any())
        {
            Assert.Fail(string.Join("\n", failingClasses));
        }
    }
}

ويستخدم طرق التمديد التالية

    public static bool HasAttributeOfType<T>(this ICustomAttributeProvider provider)
    {
        return provider.GetCustomAttributes(typeof(T), false).Length > 0;
    }

    public static IEnumerable<Type> ThisTypeAndSubClasses(this Type startingType)
    {
        var types = new List<Type>();
        foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
        {
            try
            {
                foreach (var type in assembly.GetTypes())
                {
                    if (startingType.IsAssignableFrom(type))
                    {
                        types.Add(type);
                    }
                }
            }
            catch (ReflectionTypeLoadException)
            {
                // Some assembly types are unable to be loaded when running as nunit tests.
                // Move on to the next assembly
            }
        }
        return types;
    }

قم بإزالة التنفيذ في إنشاء فئة أو استخدم الفئة الأساسية

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