简体   繁体   English

如何强制所有派生类实现抽象方法或属性?

[英]How can I force all derived classes to implement an abstract method or property?

An abstract function must be implemented by all concrete classes. 所有具体类都必须实现抽象函数。

Sometimes you want to force all derivative classes to implement the abstract function, even derivatives of concrete classes. 有时您希望强制所有派生类实现抽象函数,甚至是具体类的派生。

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

I would like the compiler to tell the programmer that the class Custom needs to implement Clone() . 我希望编译器告诉程序员Custom类需要实现Clone() Is there way? 有办法吗?

It's not possible for the compiler to enforce this. 编译器不可能强制执行此操作。 You might look at writing your own analysis plugin to Gendarme or FxCop to enforce such requirements. 您可以考虑将自己的分析插件编写到GendarmeFxCop以强制执行此类要求。

I would guess you don't really need ALL derived classes to implement the abstract method, but it definitely sounds like you have a bit of a code smell in your design. 我猜你真的不需要所有的派生类来实现抽象方法,但它肯定听起来你的设计中有一些代码味道。

If you don't have any functionality in the Concrete.Clone() method, then you can make your 'Concrete' class abstract as well (just be sure to change the name ;-). 如果您在Concrete.Clone()方法中没有任何功能,那么您也可以将“Concrete”类抽象化(只需确保更改名称;-)。 Leave out any reference of the Clone() method. 省略Clone()方法的任何引用。

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

If you have some basic functionality in the Concrete.Clone() method, but need detailed information from a higher level, then break it out into it's own abstract method or property forcing a higher level implementation to supply this information. 如果您在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成为一个抽象类来强制执行该操作。

You can check this at run-time using reflection and throw an exception to disrupt execution, wrecking havoc for "impolite" users of your library. 您可以使用反射在运行时检查此项并抛出异常以中断执行,从而破坏库中“不礼貌”用户的破坏。 Performance-wise that is not very wise, even though you could store a static HashSet<System.Type> in the base abstract class with all verified types. 即使您可以在基本抽象类中存储具有所有已验证类型的静态HashSet <System.Type> ,但性能方面并不是很明智。

I think your best bet is to provide clear documentation that tells any user of your code that it is deemed necessary to override the Clone() method. 我认为您最好的选择是提供明确的文档,告诉任何用户您的代码,认为有必要覆盖Clone()方法。

I've made the following NUnit test that uses reflection to check the implementation. 我做了以下NUnit测试,它使用反射来检查实现。 Hopefully you can adapt as needed. 希望您可以根据需要进行调整。

I suspect it won't handle overloaded methods well, but it's sufficient for what I want. 我怀疑它不会很好地处理重载方法,但它足以满足我的需求。

(Comments welcome) (欢迎评论)

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

It uses the following extension methods 它使用以下扩展方法

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

删除concreate类中的实现或使用基类

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM