繁体   English   中英

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

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

所有具体类都必须实现抽象函数。

有时您希望强制所有派生类实现抽象函数,甚至是具体类的派生。

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

我希望编译器告诉程序员Custom类需要实现Clone() 有办法吗?

编译器不可能强制执行此操作。 您可以考虑将自己的分析插件编写到GendarmeFxCop以强制执行此类要求。

我猜你真的不需要所有的派生类来实现抽象方法,但它肯定听起来你的设计中有一些代码味道。

如果您在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成为一个抽象类来强制执行该操作。

您可以使用反射在运行时检查此项并抛出异常以中断执行,从而破坏库中“不礼貌”用户的破坏。 即使您可以在基本抽象类中存储具有所有已验证类型的静态HashSet <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;
    }

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

暂无
暂无

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

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