抽象函数必须由所有具体类实现。
有时你想强迫所有派生类实现抽象函数,甚至是具体类的衍生函数。
class Base { protected abstract Base Clone(); }
class Concrete : Base { protected override Base Clone(){...}; }
class Custom : Concrete {}
我希望编译器告诉程序员类Custom
需要实现Clone()
。有办法吗?
答案 0 :(得分:12)
答案 1 :(得分:3)
我猜你真的不需要所有的派生类来实现抽象方法,但它听起来好像你的设计中有一些代码味道。
如果您在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 ...";}}
}
答案 2 :(得分:2)
您必须使Concrete
成为一个抽象类来强制执行该操作。
答案 3 :(得分:0)
您可以使用反射在运行时检查此信息并抛出异常以中断执行,从而破坏图书馆“不礼貌”用户的破坏。即使您可以在基本抽象类中存储静态 HashSet< System.Type> 并且所有已验证的类型,但性能方面并不是很明智。
我认为您最好的选择是提供明确的文档,告诉任何用户您的代码,认为有必要覆盖Clone()方法。
答案 4 :(得分:0)
我已经进行了以下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;
}
答案 5 :(得分:-2)
删除具体类中的实现或使用基类