web-dev-qa-db-ja.com

C#仮想静的メソッド

静的仮想が不可能なのはなぜですか? C#に依存しているのでしょうか、それともOOの世界では意味がないのですか?

コンセプトにはすでに下線が引かれていることは知っていますが、前の質問に対する簡単な答えは見つかりませんでした。

31
Toto

virtualは、オブジェクトの動的タイプに応じて、呼び出されたメソッドが実行時に選択されることを意味します。 staticは、メソッドを呼び出すためにオブジェクトが必要ないことを意味します。

同じ方法で両方を行うことをどのように提案しますか?

51
sbi

Eric Lippertはこれについてブログに投稿しており、いつものように彼の投稿でこのテーマを深くカバーしています。

http://blogs.msdn.com/b/ericlippert/archive/2007/06/14/calling-static-methods-on-type-parameters-is-illegal-part-one.aspx

「仮想」と「静的」は反対です! 「仮想」は「実行時型情報に基づいて呼び出されるメソッドを決定する」を意味し、「静的」は「コンパイル時の静的分析のみに基づいて呼び出されるメソッドを決定する」ことを意味します。

21
Michael Stum

静的仮想メソッドには意味がないと言う人。これがどのように可能であるかを理解していなくても、それが不可能であるという意味ではありません。これを可能にする言語があります!!たとえば、Delphiを見てください。

7
Sergey Krusch

「静的」と「仮想」の矛盾は、c#の問題にすぎません。他の多くの言語のように、「静的」が「クラスレベル」に置き換えられた場合、目隠しされることはありません。

単語の選択が悪すぎると、この点でc#が機能しなくなります。 Type.InvokeMemberメソッドを呼び出して、クラスレベルの仮想メソッドへの呼び出しをシミュレートすることは引き続き可能です。メソッド名を文字列として渡すだけです。コンパイル時のチェック、強い型付け、およびサブクラスがメソッドを実装する制御はありません。

Delphiの美しさ:

type
  TFormClass = class of TForm;
var
  formClass: TFormClass;
  myForm: TForm;
begin
  ...
  formClass = GetAnyFormClassYouWouldLike;
  myForm = formClass.Create(nil);
  myForm.Show;
end
7
Johan Nilsson

私は否定する人になるつもりです。あなたが説明していることは、技術的には言語の一部ではありません。ごめんなさい。しかし、言語内でシミュレートすることは可能です。

何を求めているのかを考えてみましょう。特定のオブジェクトにアタッチされていないメソッドのコレクションが必要であり、実行時またはコンパイル時にすべて簡単に呼び出して交換できます。

私にとって、あなたが本当に望んでいるのは、委任されたメソッドを持つシングルトンオブジェクトのように聞こえます。

例をまとめましょう:

public interface ICurrencyWriter {
    string Write(int i);
    string Write(float f);
}

public class DelegatedCurrencyWriter : ICurrencyWriter {
    public DelegatedCurrencyWriter()
    {
        IntWriter = i => i.ToString();
        FloatWriter = f => f.ToString();
    }
    public string Write(int i) { return IntWriter(i); }
    public string Write(float f) { return FloatWriter(f); }
    public Func<int, string> IntWriter { get; set; }
    public Func<float, string> FloatWriter { get; set; }
}

public class SingletonCurrencyWriter {
    public static DelegatedCurrencyWriter Writer {
        get {
            if (_writer == null)
               _writer = new DelegatedCurrencyWriter();
            return _writer;
        }
    }
}

使用中で:

Console.WriteLine(SingletonCurrencyWriter.Writer.Write(400.0f); // 400.0

SingletonCurrencyWriter.Writer.FloatWriter = f => String.Format("{0} bucks and {1} little pennies.", (int)f, (int)(f * 100));

Console.WriteLine(SingletonCurrencyWriter.Writer.Write(400.0f); // 400 bucks and 0 little pennies

これらすべてを考慮すると、通貨値を書き出すシングルトンクラスができたので、その動作を変更できます。私は基本的にコンパイル時に動作を定義しましたconventionコンパイル時(コンストラクター内)または実行時のいずれかで動作を変更できるようになりました。つまり、取得しようとしている効果を信じています。 。動作の継承が必要な場合は、バックチェーンを実装することでそれを行うことができます(つまり、新しいメソッドで前のメソッドを呼び出すようにします)。

そうは言っても、上記のサンプルコードは特にお勧めしません。一つには、それはスレッドセーフではなく、人生を正気に保つための場所が本当にたくさんありません。この種の構造へのグローバルな依存は、グローバルな不安定性を意味します。これは、Cの薄暗い時代に変更可能な動作が実装された多くの方法の1つです。関数ポインターの構造体、この場合は単一のグローバル構造体です。

5
plinth

はい、可能です。

そのための最も必要なユースケースは、「オーバーライド」できるファクトリを持つことです。

これを行うには、 F-bounded polymorphism を使用してジェネリック型パラメーターに依存する必要があります。

例1工場の例を見てみましょう:

class A: { public static A Create(int number) { return ... ;} }
class B: A { /* How to override the static Create method to return B? */}

また、createBにアクセスして、BクラスのBオブジェクトを返す必要があります。または、Aの静的関数をBで拡張可能なライブラリにすることもできます。解決策:

class A<T> where T: A<T> { public static T Create(int number) { return ...; } }
class B: A<B>  { /* no create function */ }
B theb = B.Create(2);       // Perfectly fine.
A thea = A.Create(0);       // Here as well

例2(高度):値の行列を乗算する静的関数を定義しましょう。

public abstract class Value<T> where T : Value<T> {
  //This method is static but by subclassing T we can use virtual methods.
  public static Matrix<T> MultiplyMatrix(Matrix<T> m1, Matrix<T> m2) {
    return // Code to multiply two matrices using add and multiply;
  }
  public abstract T multiply(T other);
  public abstract T add(T other);
  public abstract T opposed();
  public T minus(T other) {
    return this.add(other.opposed());
  }
}
// Abstract override
public abstract class Number<T> : Value<T> where T: Number<T> {
  protected double real;

  /// Note: The use of MultiplyMatrix returns a Matrix of Number here.
  public Matrix<T> timesVector(List<T> vector) {
    return MultiplyMatrix(new Matrix<T>() {this as T}, new Matrix<T>(vector));
  }
}
public class ComplexNumber : Number<ComplexNumber> {
  protected double imag;
  /// Note: The use of MultiplyMatrix returns a Matrix of ComplexNumber here.
}

これで、静的MultiplyMatrixメソッドを使用して、ComplexNumberから直接複素数の行列を返すこともできます。

Matrix<ComplexNumber> result = ComplexNumber.MultiplyMatrix(matrix1, matrix2);
2
Mikaël Mayer

技術的にはstatic virtualメソッドを定義することはできませんが、ここですでに指摘したすべての理由により、C#を使用してみたと思うことを機能的に達成できます。拡張メソッド。

MSDNから:

拡張メソッドを使用すると、新しい派生型を作成したり、再コンパイルしたり、元の型を変更したりすることなく、既存の型にメソッドを「追加」できます。

詳細については、 C#拡張メソッド(C#プログラミングガイド) を確認してください。

2
Zach Bonham

.NETでは、仮想メソッドのディスパッチは、実行時にメソッドが呼び出されたときにオブジェクトの実際の型を調べ、クラスのvtableから最もオーバーライドするメソッドを見つけることによって(大まかに)実行されます。静的クラスを呼び出す場合、チェックするオブジェクトインスタンスがないため、ルックアップを実行するvtableがありません。

1
thecoop

提示されたすべてのオプションを要約すると:

0
ivan_pozdeev