J'ai fait le test NUnit suivant qui utilise la réflexion pour vérifier la mise en œuvre. J'espère que vous pourrez vous adapter au besoin.
Je suppose qu'il ne gérera pas bien les méthodes surchargées, mais c'est suffisant pour ce que je veux.
(commentaires sont les bienvenus)
/// <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));
}
}
}
Il utilise les méthodes d'extension suivantes
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;
}
Tant que une classe concrète dans l'arbre d'héritage a mis en œuvre la méthode abstraite, pourquoi voudriez-vous si un enfant de cette classe concrète repose sur la mise en œuvre de ses parents? –
Je suis en retard à la fête mais j'ai le même problème.J'aimerais avoir un qualificateur 'private abstract' qui oblige chaque classe dérivée à implémenter la fonction mais empêche le code de classe de retomber implicitement dans l'implémentation d'une classe parente. 'Clone()' est un bon exemple. Mon cas ressemble plus à 'ToString()'. Quelque chose de générique mais distinct par classe. –
En fait, cela doit se produire plus souvent (ou il me manque quelque chose). –