Как я могу заставить все полученные классы реализовать абстрактный метод или свойство?

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 (), то вы также можете сделать свой «конкретный» класс абстрактным (просто обязательно измените имя ;-). Оставьте любую ссылку метода 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 абстрактный класс, чтобы обеспечить соблюдение этого.

Вы можете проверить это во время выполнения, используя отражение и бросить исключение, чтобы нарушить выполнение, что разрушает хаос для «невежливых» пользователей вашей библиотеки. Производительность, которая не очень мудр, даже если вы можете хранить статический Хэшсетu003CSystem.Type> В базовом абстрактном классе со всеми проверенными типами.

Я думаю, что ваш лучший выбор - предоставить четкую документацию, которая сообщает любому пользователю вашего кода, что он считается необходимым для переопределения метода клона ().

Я сделал следующий тест 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