web-dev-qa-db-ja.com

仮想および静的なC#メソッドを宣言できないのはなぜですか?

単なる静的メソッドの束であるヘルパークラスがあり、ヘルパークラスをサブクラス化したいと考えています。一部の動作はサブクラスに応じて一意であるため、基本クラスから仮想メソッドを呼び出したいのですが、すべてのメソッドが静的であるため、単純な仮想メソッドを作成できません(仮想メソッドにアクセスするにはオブジェクト参照が必要です)。

これを回避する方法はありますか?私はシングルトンを使用できると思います。HelperClass.Instance.HelperMethod()はHelperClass.HelperMethod()よりそれほど悪くはありません。ブラウニーは、仮想静的メソッドをサポートするいくつかの言語を指摘できる人を指します。

編集: OK Googleの検索結果から、私は少しそこにいないと思っていました。

49
Luke

仮想静的メソッドは意味がありません。 HelperClass.HelperMethod();を呼び出すと、なんらかのランダムサブクラスのメソッドが呼び出されるのはなぜですか? HelperClassの2つのサブクラスがある場合、ソリューションは実際に故障します。どちらを使用しますか?

オーバーライド可能な静的タイプのメソッドが必要な場合は、おそらく以下を使用する必要があります。

  • 同じサブクラスをグローバルに使用する場合のシングルトン。
  • アプリケーションのさまざまな部分でさまざまな動作が必要な場合は、ファクトリまたは依存性注入を使用した伝統クラス階層。

状況に合ったソリューションを選択してください。

42

私はあなたが狂っているとは思わない。 .NETでは現在不可能なものを使用したいだけです。

ジェネリックについて話している場合、仮想静的メソッドの要求は非常に意味があります。たとえば、CLRデザイナーに対する私の将来の要求は、次のようなインターフェイスを作成できるようにすることです。

public interface ISumable<T>
{
  static T Add(T left, T right);
}

次のように使用します:

public T Aggregate<T>(T left, T right) where T : ISumable<T>
{
  return T.Add(left, right);
}

しかし、今は不可能ですので、私は次のようにやっています:

    public static class Static<T> where T : new()
    {
      public static T Value = new T();
    }

    public interface ISumable<T>
    {
      T Add(T left, T right);
    }

    public T Aggregate<T>(T left, T right) where T : ISumable<T>, new()
    {
      return Static<T>.Value.Add(left, right);
    }
32
SeeR

実際、これはDelphiで実行できます。例:

type
  TForm1 = class(TForm)
    procedure FormShow(Sender: TObject);
  end;

  TTestClass = class
  public
    class procedure TestMethod(); virtual;
  end;

  TTestDerivedClass = class(TTestClass)
  public
    class procedure TestMethod(); override;
  end;

  TTestMetaClass = class of TTestClass;

var
  Form1: TForm1;

implementation

{$R *.dfm}

class procedure TTestClass.TestMethod();
begin
  Application.MessageBox('base', 'Message');
end;

class procedure TTestDerivedClass.TestMethod();
begin
  Application.MessageBox('descendant', 'Message');
end;


procedure TForm1.FormShow(Sender: TObject);
var
  sample: TTestMetaClass;
begin
  sample := TTestClass;
  sample.TestMethod;
  sample := TTestDerivedClass;
  sample.TestMethod;
end;

とても興味深い。私はもうDelphiを使用していませんが、メタクラス機能を使用してカスタムデザイナーキャンバスにさまざまなタイプのコントロールを非常に簡単に作成できたことを思い出します。 TButton、TTextBoxなどはパラメーターであり、実際のメタクラス引数を使用して適切なコンストラクターを呼び出すことができました。

貧乏人の工場パターンの種類:)

15
Alan

通常の静的メソッドを使用するだけで同じ効果を達成し、newキーワードでシャドウすることができます

public class Base 
{
    //Other stuff

    public static void DoSomething()
    {
        Console.WriteLine("Base");
    }
}

public class SomeClass : Base
{
    public new static void DoSomething()
    {
        Console.WriteLine("SomeClass");
    }
}
public class SomeOtherClass : Base
{
}

その後、次のようにメソッドを呼び出すことができます

Base.DoSomething(); //Base
SomeClass.DoSomething(); //SomeClass
SomeOtherClass.DoSomething(); //Base
12
Davy8

私はDelphiから来ましたが、これはc#で見逃している多くの機能の1つです。 Delphiでは、型付きの型参照を作成でき、親クラスの型が必要な場所であればどこでも派生クラスの型を渡すことができます。オブジェクトとしての型のこの処理には、強力なユーティリティがありました。特に、メタデータの実行時決定を可能にします。私はここで構文を恐ろしく混ぜていますが、C#では次のようになります:

    class Root {
       public static virtual string TestMethod() {return "Root"; }
    }
    TRootClass = class of TRoot; // Here is the typed type declaration

    class Derived : Root {
       public static overide string TestMethod(){ return "derived"; }
    }

   class Test {
        public static string Run(){
           TRootClass rc;
           rc = Root;
           Test(rc);
           rc = Derived();
           Test(rc);
        }
        public static Test(TRootClass AClass){
           string str = AClass.TestMethod();
           Console.WriteLine(str);
        }
    } 

生成される:ルート派生

8
Ken Revak

静的メソッドはクラスのインスタンスの外部に存在します。非静的データは使用できません。

仮想メソッドは、インスタンスのtypeに応じて、オーバーロードされた関数によって「上書き」されます。

したがって、静的と仮想の間に明確な矛盾があります。

これはサポートの問題ではなく、概念です。

更新:ここで間違っていることが判明しました(コメントを参照):

したがって、仮想静的メソッドをサポートするOOP-Languageが見つかるとは思いません。

7
Peter Parker

あなたは狂っていません。参照しているものは、遅延静的バインディングと呼ばれます。最近PHPに追加されました。それを説明するすばらしいスレッドがあります-ここに: いつ静的遅延バインディングを使用する必要がありますか?

6
Alex Weinstein

オーバーライドメソッドは、基本クラスから継承されたメンバーの新しい実装を提供します。オーバーライド宣言によってオーバーライドされるメソッドは、オーバーライドされたベースメソッドと呼ばれます。オーバーライドされたベースメソッドには、オーバーライドメソッドと同じシグネチャが必要です。非仮想メソッドまたは静的メソッドをオーバーライドすることはできません。オーバーライドされた基本メソッドは、仮想、抽象、またはオーバーライドである必要があります。

オーバーライド宣言は、仮想メソッドのアクセシビリティを変更できません。オーバーライドメソッドと仮想メソッドの両方に同じアクセスレベル修飾子が必要です。

New、static、またはvirtual修飾子を使用してオーバーライドメソッドを変更することはできません。

オーバーライドするプロパティ宣言では、継承するプロパティとまったく同じアクセス修飾子、タイプ、および名前を指定する必要があり、オーバーライドするプロパティは仮想、抽象、またはオーバーライドである必要があります。

1
user2761122

newの代わりにキーワードvirtualを使用して、メソッドまたはメンバーの仮想と静的を実際に組み合わせることができます。

次に例を示します。

class Car
{
    public static int TyreCount = 4;
    public virtual int GetTyreCount() { return TyreCount; }
}
class Tricar : Car
{
    public static new int TyreCount = 3;
    public override int GetTyreCount() { return TyreCount; }
}

...

Car[] cc = new Car[] { new Tricar(), new Car() };
int t0 = cc[0].GetTyreCount(); // t0 == 3
int t1 = cc[1].GetTyreCount(); // t1 == 4

明らかに、オーバーライドされたTyreCountメソッドでGetTyreCount値を設定できたかもしれませんが、これにより値の重複が回避されます。クラスとクラスインスタンスの両方から値を取得することが可能です。

今、誰かがその機能の本当にインテリジェントな使用法を見つけることができますか?

1
Mart

仮想メソッドは、インスタンス化されたオブジェクトの定義された型を使用して、実行する実装を決定するため(参照変数の宣言された型とは対照的に)

...そして、静的であり、もちろん、インスタンス化されたクラスのインスタンスさえあれば気にしないことです...

したがって、これらは互換性がありません。

要するに、インスタンスがどのサブクラスであるかに基づいて動作を変更する場合、メソッドは静的メソッドではなく、基本クラスの仮想メソッドである必要があります。

ただし、これらの静的メソッドが既にあり、それらをオーバーライドする必要があるため、これにより問題を解決できます:静的メソッドに単純に委任する仮想インスタンスメソッドを基本クラスに追加し、それらの仮想インスタンスラッパーメソッドをオーバーライドします(静的サブクラスではなく)必要に応じて、各派生サブクラスで...

1
charles bretana

抽象ジェネリッククラスから「抽象静的」メソッドの継承を強制する方法があります。以下を参照してください。

public abstract class Mother<T> where T : Mother<T>, new()
{
    public abstract void DoSomething();

    public static void Do()
    {
        (new T()).DoSomething();
    }

}

public class ChildA : Mother<ChildA>
{
    public override void DoSomething() { /* Your Code */ }
}

public class ChildB : Mother<ChildB>
{
    public override void DoSomething() { /* Your Code */ }
}

例(前のマザーを使用):

public class ChildA : Mother<ChildA>
{
    public override void DoSomething() { Console.WriteLine("42"); }
}

public class ChildB : Mother<ChildB>
{
    public override void DoSomething() { Console.WriteLine("12"); }
}

public class Program
{
    static void Main()
    {
        ChildA.Do();  //42
        ChildB.Do();  //12
        Console.ReadKey();
    }
}

1つの抽象クラスからのみ継承でき、new()実装に寛容であることを要求するため、それほど素晴らしいものではありません。

さらに、継承したクラスのサイズによっては、メモリに関してコストが高くなると思います。メモリに問題がある場合は、新しいメソッドの後にすべてのプロパティ/変数をパブリックメソッドに設定する必要があります。これは、デフォルト値を設定するのにひどい方法です。

1
Lostblue

Delphiはこのようなものをサポートしていると聞きました。メタクラスのクラスオブジェクトインスタンスを作成することで実行されるようです。

私はそれが機能するのを見たことがないので、私はそれが機能するかどうか、またはそれが何のためにポイントであるかわからない。

追伸私のドメインではないため、間違っている場合は修正してください。

1
rslite

Martは「新しい」キーワードでそれを正しく理解しました。このタイプの機能が必要であり、Martのソリューションはうまく機能するため、実際にここに来ました。実際、プログラマーにこのフィールドを提供するように強制するために、基本クラスメソッドを抽象化しました。

私のシナリオは次のとおりです。

基本クラスのHouseDeedがあります。各HouseタイプはHouseDeedから派生し、価格が必要です。

以下は、部分ベースのHouseDeedクラスです。

public abstract class HouseDeed : Item
{
    public static int m_price = 0;
    public abstract int Price { get; }
    /* more impl here */
}

次に、2つの派生したハウスタイプを見てみましょう。

public class FieldStoneHouseDeed : HouseDeed
{
    public static new int m_price = 43800;
    public override int Price { get { return m_price; } }
    /* more impl here */
}

そして...

public class SmallTowerDeed : HouseDeed
{
    public static new int m_price = 88500;
    public override int Price { get { return m_price; } }
    /* more impl here */
}

ご覧のように、SmallTowerDeed.m_price型とインスタンスnew SmallTowerDeed()。Priceを介して家の価格にアクセスできます。また、抽象的であるため、このメカニズムはプログラマーに新しい派生家の各タイプの価格を提供させます。

誰かが「静的仮想」と「仮想」が概念的に互いに対立していることを指摘しました。同意しません。この例では、静的メソッドはインスタンスデータにアクセスする必要がないため、(1)TYPEのみを介して価格を利用でき、(2)価格を提供するという要件が満たされています。

1
luket

新しいキーワードを使用できます

namespace AspDotNetStorefront
{
    // This Class is need to override StudioOnlineCommonHelper Methods in a branch
    public class StudioOnlineCommonHelper : StudioOnlineCore.StudioOnlineCommonHelper
    {
        //
        public static new void DoBusinessRulesChecks(Page page)
        {
            StudioOnlineCore.StudioOnlineCommonHelper.DoBusinessRulesChecks(page);
        }
    }
}
0
Dennis Walter