カスタムタイプの使用を妨げる抽象化、実装で従うべきルールは何ですか?
-
28-09-2019 - |
質問
アプリケーション内のC#スクリプトで使用するためのカスタムタイプシステムを構築しました。スクリプトはオンザフライでコンパイルされており、アプリケーション内部データとの相互作用を可能にします。このタイプシステムは、次のようなインターフェイスを使用して抽象的な方法で設計されています IValue
. 。の実装 IValue
a RefString
, RefInteger
, RefDouble
(他の多くの中でも、これは私の問題を示すのに十分です)。
今、私たちは私が立ち往生しているところに来ます...これらの使用 IValue
オブジェクトはやや不自然です。インターフェイスを常に使用してオブジェクトと対話するための優れた設計と考えられていますが、インターフェイスのオペレーターを暗黙的な変換または過負荷を定義する可能性はありません。これは、適切なオペレーターが使用されるように醜い明示的なキャストが避けられない状況に屈します。
例:
IValue Add(IValue a, IValue b)
{
//return a+b; // won't work: which operator +() to use?
return (RefInteger)a + (RefInteger)b;
}
値タイプを含む式のC#の場合、暗黙的な変換が提供されます。このようなカスタムシステムを設計する良い方法は何ですか?
タイプシステムを削除することを書き直しました IValue
インターフェイスと導入a RefValue
基本クラス。このようにして、すでに明示的なキャストの一部を排除できます。このベースクラスでオペレーターの過負荷の一部を実装しましたが、それはデフォルトの変換オペレーターに多くのトラブルを引き起こしました...これに加えて、オペレーターの実装に実装するロジックは、システム内のタイプに関する多くの知識を暗示しています。これはどういうわけか、それが行かなければならない方法だと思いますが、これを良く安全な方法で実装するためのルールは何ですか?
編集: :しばらく苦労した後、私が見つけることができるルールのいくつかは次のとおりです。
- ベースタイプ(int、double、string、...)からの変換演算子のみを暗黙的に宣言します
- ベースタイプへの変換を明示的に宣言します(暗黙のキャストを避けるために!!
- あいまいな呼び出しを避けるために、 +、 - 、 /、 *演算子を基本クラスおよび派生クラスでオーバーライドしないでください。 [それでは行く方法は何ですか?派生クラスで操作オーバーロードを行いましたが、これには使用時のキャストが必要です。
解決
あなたが作ることができるはずなら Add
すべての操作 IValue
s、おそらくインターフェイスにはanを含める必要があります Add
方法?そうすれば、できます return a.Add(b);
, 、および各タイプに操作を実行する方法の知識を押し進めます。
1つの問題は、今見ているように、どこで電話をかけることができるということです。 a
は RefString
と b
は RefInteger
, 、おそらくあなたが望むものではありません。ジェネリックはそれを修正するのに役立ちます:
T Add<T>(T a, T b) where T : IValue
{
return a.Add(b);
}
(もちろん、必要に応じてヌルチェックを追加する必要があります)