次の (大幅に簡略化された) コードを検討してください。
public T Function<T>() {
if (typeof(T) == typeof(string)) {
return (T) (object) "hello";
}
...
}
object
最初に にキャストし、次に にキャストするのはちょっとばかげていT
ます。T
しかし、コンパイラは、保証された前のテストがタイプ であることを知る方法がありませんstring
。
C#でこの動作を実現する最もエレガントで慣用的な方法は何ですか(使用できないtypeof(T) == typeof(string)
ため、愚かなを取り除くことも含まれます)?T is string
補遺: .net には戻り値の型の差異がないため、関数を文字列型にオーバーロードすることはできません (ちなみに、これは単なる例ですが、UML などのポリモーフィズムで関連付け終了の再定義ができる理由の 1 つです)。 c# では実行できません)。明らかに、以下は素晴らしいことですが、うまくいきません。
public T Function<T>() {
...
}
public string Function<string>() {
return "hello";
}
具体例 1:特定の型をテストするジェネリック関数がジェネリックではないという事実に対する攻撃がいくつかあったため、より完全な例を提供しようとします。Type-Square 設計パターンを考えてみましょう。以下にスニペットを示します。
public class Entity {
Dictionary<PropertyType, object> properties;
public T GetTypedProperty<T>(PropertyType p) {
var val = properties[p];
if (typeof(T) == typeof(string) {
(T) (object) p.ToString(this); // magic going here
}
return (T) TypeDescriptor.GetConverter(typeof(T)).ConvertFrom(val);
}
}
具体例 2:インタープリターの設計パターンを考えてみましょう。
public class Expression {
public virtual object Execute() { }
}
public class StringExpression: Expression {
public override string Execute() { } // Error! Type variance not allowed...
}
次に、Execute でジェネリックを使用して、呼び出し元が戻り値の型を強制できるようにします。
public class Expression {
public virtual T Execute<T>() {
if(typeof(T) == typeof(string)) { // what happens when I want a string result from a non-string expression?
return (T) (object) do_some_magic_and_return_a_string();
} else if(typeof(T) == typeof(bool)) { // what about bools? any number != 0 should be True. Non-empty lists should be True. Not null should be True
return (T) (object) do_some_magic_and_return_a_bool();
}
}
}
public class StringExpression: Expressiong {
public override T Execute<T>() where T: string {
return (T) string_result;
}
}