静的仮想が不可能なのはなぜですか?C#に依存しているのですか、それともOOの世界では意味がありませんか?
コンセプトにはすでに下線が引かれていることは知っていますが、前の質問に対する簡単な答えは見つかりませんでした。
virtual
オブジェクトの動的タイプに応じて、呼び出されたメソッドが実行時に選択されることを意味します。static
メソッドを呼び出すためにオブジェクトが必要ないことを意味します。
同じ方法で両方を行うことをどのように提案しますか?
Eric Lippertには、これに関するブログ投稿があります。いつものように、彼の投稿では、このテーマについて深く取り上げています。
「仮想」と「静的」は反対です!「仮想」は「実行時型情報に基づいて呼び出されるメソッドを決定する」を意味し、「静的」は「コンパイル時の静的分析のみに基づいて呼び出されるメソッドを決定する」ことを意味します。</ p>
「静的」と「仮想」の矛盾は、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
静的仮想メソッドには意味がないと言う人たち-これがどのように可能であるかを理解していなくても、それが不可能であるという意味ではありません。これを可能にする言語があります!! たとえば、Delphiを見てください。
I'm going to be the one who naysays. What you are describing is not technically part of the language. Sorry. But it is possible to simulate it within the language.
Let's consider what you're asking for - you want a collection of methods that aren't attached to any particular object that can all be easily callable and replaceable at run time or compile time.
To me that sounds like what you really want is a singleton object with delegated methods.
Let's put together an example:
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;
}
}
}
in use:
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
Given all this, we now have a singleton class that writes out currency values and I can change the behavior of it. I've basically defined the behavior convention at compile time and can now change the behavior at either compile time (in the constructor) or run time, which is, I believe the effect you're trying to get. If you want inheritance of behavior, you can do that to by implementing back chaining (ie, have the new method call the previous one).
That said, I don't especially recommend the example code above. For one, it isn't thread safe and there really isn't a lot in place to keep life sane. Global dependence on this kind of structure means global instability. This is one of the many ways that changeable behavior was implemented in the dim dark days of C: structs of function pointers, and in this case a single global struct.
はい、可能です。
そのための最も望まれるユースケースは、「オーバーライド」できるファクトリを持つことです。
これを行うには、F有界量化を使用するジェネリック型パラメーターに依存する必要があります。
例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);
技術的には静的仮想メソッドを定義することはできませんが、ここですでに指摘したすべての理由により、C#拡張メソッドを使用して試みていると私が思うことを機能的に達成できます。
Microsoft Docsから:
拡張メソッドを使用すると、新しい派生型を作成したり、再コンパイルしたり、元の型を変更したりすることなく、既存の型にメソッドを「追加」できます。
詳細については、拡張メソッド(C#プログラミングガイド)を確認してください。
.NETでは、仮想メソッドのディスパッチは、実行時にメソッドが呼び出されたときにオブジェクトの実際のタイプを調べ、クラスのvtableから最もオーバーライドするメソッドを見つけることによって(大まかに)実行されます。静的クラスを呼び出す場合、チェックするオブジェクトインスタンスがないため、ルックアップを実行するvtableはありません。
提示されたすべてのオプションを要約すると、次のようになります。
これはC#の一部ではありません。これは、C以降(およびおそらくそれ以前)のように、static
「実行時に何にもバインドされない」ことを意味するためです。static
エンティティは宣言型にバインドされます(したがって、他のstatic
エンティティにアクセスできます)が、コンパイル時のみです。
static
同等のもの(必要な場合)が代わりに「実行時に型オブジェクトにバインドされる」ことを意味する他の言語で可能です。例としては、Delphi、Python、PHPなどがあります。これは、次のように分類できるいくつかの方法でエミュレートできます。