目的のC#ポリモーフィズムでコンパクトにできないのはなぜですか?
-
06-07-2019 - |
質問
私がやりたいことは次のとおりです。
XmlWriter writer = XmlWriter.Create(
(string.IsNullOrEmpty(outfile) ? Console.Out : outfile)
);
これはコンパイルされませんが、「System.IO.TextWriter」と「string」"の間に暗黙的な変換がないため、エラー「"条件式のタイプ」を判別できません。上記のコードは次の単純化です:
XmlWriter writer;
if (string.IsNullOrEmpty(outfile))
{
writer = XmlWriter.Create(Console.Out); // Constructor takes TextWriter
}
else
{
writer = XmlWriter.Create(outfile); // Constructor takes string
}
Create
へのこれら2つの呼び出しは完全に有効であり、コンパイルされます。インラインテストでやろうとしていたように、これをもっとコンパクトにする方法はありますか?
自分が望むものが機能しないことは私には意味がありません。これを精神的に考えると、コンパイラは string.IsNullOrEmpty(outfile)
を評価して、どのケースをとるかを判断するようです:
- 条件が真の場合、
Console.Out
に進み、TextWriterを受け取るXmlWriter.Create
のバージョンを多態的に選択する必要があることを確認します。 。 - 条件が偽の場合、
outfile
を使用して、文字列を取るXmlWriter.Create
のバージョンを多態的に選択する必要があることを確認します。
MLでのプログラミングは私の脳をゆがめましたか?
解決
それができない理由は、コンパイラがコンパイル時に使用するCreateのオーバーロードを選択する必要があるためです-アプローチでは実行時に実行する必要があります。最短時間はおそらく次のとおりです。
XmlWriter writer = String.IsNullOrEmpty(outfile)
? XmlWriter.Create(Console.Out)
: XmlWriter.Create(outfile);
他のヒント
誰もが以下を提案しているようです:
XmlWriter writer = String.IsNullOrEmpty(outfile)
? XmlWriter.Create(Console.Out)
: XmlWriter.Create(outfile);
ただし、これも実行可能です:
XmlWriter writer = XmlWriter.Create(string.IsNullOrEmpty(outfile)
? Console.Out : new StreamWriter(outfile));
後者は元の試みに近く、IMOはよりコンパクトです。
C#コンパイラは、コンパイル中に静的に実行する方法を選択します。コンパイル時に生成されるILは、特定のメソッドへの参照です。ポリモーフィズム部分は、実行時に特定の機能の実装を選択するときに実行時に入ります。
あなたの?:ステートメントは実行時に評価されるため、コンパイラは実行するメソッドを判断できません。
これに変更すると動作します。
XmlWriter writer = string.IsNullOrEmpty(outfile) ?
XmlWriter.Create(Console.Out) :
XmlWriter.Create(outfile);
問題は、コンパイル時に何を判断できないかです
(string.IsNullOrEmpty(outfile) ? Console.Out : outfile)
戻る必要があります。それは文字列になるのでしょうか、それともTextWriterになるのでしょうか?これは実行時にしか判断できないため、?演算子はコンパイル時に解決する必要があります。
あなたがそれから抜け出すことができる最善の方法はおそらく次のとおりです:
XmlWriter writer = string.IsNullOrEmpty(outfile)
? XmlWriter.Create(Console.Out)
: XmlWriter.Create(outfile);
ここでいくつかのことが起きています。
まず、「例外」使用している場所ではなく、Ternary Operator(tm)が原因で発生しています。問題は、単一の共通の基本型(オブジェクト以外)に解決できない2つの異なる型を返そうとする単一の式を持っているためです。
さらに、コンストラクターのオーバーロードは、おそらくまったく関係のない2つの完全に異なる型を取ります。 C#コンパイラは非常にスマートです。非常にスマートですが、それほどスマートではありません。
いいえ、2つの別個のコンストラクターであるため、2つの別個の呼び出しを行う必要があります。
呼び出すオーバーロードはコンパイル時に決定されるため、実行時に異なるオーバーロードを呼び出すデータ型を選択することはできません。
また、条件演算子は単一のデータ型のみを返すことができ、選択に応じて異なるデータ型を返すことはできません。
C#は静的に型指定されており、すべてのポリモーフィズムがコンパイル時に発生します。また、コンパイル時には条件式のタイプは不明です。