2009-07-16 16 views
7

抽象関数は、すべての具象クラスで実装する必要があります。すべての派生クラスに強制的に抽象メソッドまたはプロパティを実装できますか?

時には、すべての派生クラスにコンクリートクラスの派生物でも抽象関数を強制的に実行させたいことがあります。

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

私はクラスCustomClone()を実装する必要があり、プログラマに伝えるために、コンパイラをしたいと思います。方法はありますか?

+0

を使用していますその具体的なクラスの親の実装に依存していますか? –

+0

私はパーティーに遅れていますが、同じ問題があります。私は、すべての派生クラスに関数を実装させるが、クラスのコードは暗黙的に親クラスの実装に戻ってしまうのを防ぐ 'private abstract'修飾子が必要です。 'Clone()'は良い例です。私の場合は 'ToString()'のようなものです。ジェネリックだがクラスごとに異なるもの。 –

+0

実際には、これはより頻繁に発生する必要があります(または何かが欠落しています)。 –

答えて

12

コンパイラがこれを強制することはできません。このような要件を満たすには、独自の分析プラグインをGendarmeまたはFxCopに書き込むことがあります。

+0

プラグインは、すべてのデリバティブまたはその逆のデリバリを強制したい場合をどのように区別しますか? –

+0

アトリビュートは、最も簡単なアプローチ - [MustImplement]または類似のものです。 –

2

これを強制するには、Concreteを抽象クラスにする必要があります。

+0

目的は簡単に拡張できる基底クラスを持つことです。あなたは、人々があなたのクラスをどのように拡張するのかを制御することはできません。あなたは周りにいないかもしれません。一番の賞は、コンパイラが実行できるコードの要件を文書化することです。 –

0

リフレクションを使用して実行時にこれを確認し、例外をスローして実行を中断させ、ライブラリの「無礼な」ユーザーを酷使することができます。すべての検証済みの型を持つ基本抽象クラスにstatic HashSet <System.Type>を格納することはできますが、パフォーマンス面ではそれほど賢明ではありません。

私はあなたの最善の策は、あなたのコードを任意のユーザーに教えて、Clone()メソッドをオーバーライドする必要があることを伝える明確なドキュメントを提供することです。

3

抽象メソッドを実装するために、すべての派生クラスは本当に必要ないと思いますが、デザインにコードの臭いがあるようです。

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 ...";}} 
} 
+0

私の例であるCloneメソッドがBaseを返すように修正されました。仮想クローンメソッドを使用してオブジェクトの複製を実装する場合、抽象メソッドを実装するには*すべての*派生クラスが必要です。 –

-2

リムーブ実装

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; 
    } 
関連する問題