.NETプロパティに関する設計上の問題があります。
_interface IX
{
Guid Id { get; }
bool IsInvalidated { get; }
void Invalidate();
}
_
問題:
このインターフェイスには、2つの読み取り専用プロパティId
とIsInvalidated
があります。ただし、それらが読み取り専用であること自体は、それらの値が一定であるという保証はありません。
それを非常に明確にすることが私の意図であったとしましょう…
Id
は定数値(したがって安全にキャッシュできる)を表しますが、IsInvalidated
は、IX
オブジェクトの存続期間中に値を変更する可能性があります(したがって、キャッシュしないでください)。_interface IX
_を変更して、その契約を十分に明示的にするにはどうすればよいですか?
ソリューションでの私自身の3つの試み:
インターフェースはすでにうまく設計されています。 Invalidate()
と呼ばれるメソッドの存在により、プログラマーは、類似した名前のプロパティIsInvalidated
の値がその影響を受ける可能性があると推測できます。
この引数は、メソッドとプロパティの名前が同じである場合にのみ保持されます。
このインターフェイスをイベントIsInvalidatedChanged
で拡張します。
_bool IsInvalidated { get; }
event EventHandler IsInvalidatedChanged;
_
IsInvalidated
の_…Changed
_イベントの存在は、このプロパティがその値を変更する可能性があることを示し、Id
の同様のイベントが存在しないことは、そのプロパティがそのプロパティを変更しないことを約束します値。
私はこのソリューションが好きですが、まったく使用できない可能性がある追加の要素がたくさんあります。
プロパティIsInvalidated
をメソッドIsInvalidated()
に置き換えます。
_bool IsInvalidated();
_
これは微妙な変更かもしれません。毎回新たに値が計算されるというヒントになるはずです。定数の場合は必要ありません。 MSDNトピック "プロパティとメソッド間の選択" は、これについて次のように述べています。
以下の状況では、プロパティではなくメソッドを使用してください。 […]パラメータが変更されていなくても、呼び出されるたびに異なる結果が返されます。
どのような答えが期待できますか?
私は、問題に対するまったく異なる解決策と、それらが私の上記の試みにどのように打ち勝ったかについての説明に最も興味があります。
私の試みに論理的な欠陥があるか、まだ言及されていない重大な欠点があり、解決策が1つしか残っていない(またはまったく解決されていない)場合は、どこで問題があったのかを知りたいと思います。
欠陥が軽微で、考慮に入れても解決策が複数ある場合は、コメントしてください。
少なくとも、どちらがあなたの好ましい解決策であり、どのような理由であるのかについてフィードバックをお願いします。
1と2ではなくソリューション3を使用します。
ソリューション1の私の問題は:Invalidate
メソッドがない場合はどうなりますか? _DateTime.Now > MyExpirationDate
_を返すIsValid
プロパティを持つインターフェースを想定します。ここでは明示的なSetInvalid
メソッドは必要ないかもしれません。メソッドが複数のプロパティに影響する場合はどうなりますか? IsOpen
およびIsConnected
プロパティを持つ接続タイプを想定します。どちらもClose
メソッドの影響を受けます。
ソリューション2:イベントの唯一の目的が、同様の名前のプロパティが各呼び出しで異なる値を返す可能性があることを開発者に通知することである場合、私はそれに対して強くアドバイスします。インターフェイスは短く明確にしておく必要があります。さらに、すべての実装がそのイベントを起動できるわけではありません。上記のIsValid
の例を再利用します。タイマーを実装し、MyExpirationDate
に到達したときにイベントを発生させる必要があります。結局のところ、そのイベントがパブリックインターフェイスの一部である場合、インターフェイスのユーザーはそのイベントが機能することを期待します。
それはこれらの解決策について言われている:それらは悪くない。メソッドまたはイベントの存在は、同様の名前のプロパティが各呼び出しで異なる値を返す可能性があることを示します。私が言っているのは、それらだけでは常にその意味を伝えるのに十分ではないということです。
ソリューションは私が目指していることです。 avivが述べたように、これはC#開発者にしか機能しないかもしれません。 C#-devとしての私にとって、IsInvalidated
がプロパティではないという事実は、「単なるアクセサーではなく、ここで何かが起こっている」という意味をすぐに伝えます。ただし、これはすべての人に当てはまるわけではなく、MainMaが指摘したように、.NETフレームワーク自体はここでは一貫していません。
ソリューション3を使用したい場合は、それを規則として宣言し、チーム全体にそれに従うことをお勧めします。 Documentationも必要だと思います。私の意見では、値の変更を示唆することは実際には非常に簡単です: "値がstill validであればtrueを返します"、 " 接続がすでに開かれているかどうかを示します "対"これがtrueを返しますオブジェクトis有効 "。ドキュメンテーションでより明示的にしても、害はありません。
だから私のアドバイスは:
ソリューション3を慣例として宣言し、それに従ってください。プロパティのドキュメントで、プロパティに変化する値があるかどうかを明確にします。単純なプロパティを扱う開発者は、それらが不変であると正しく想定します(つまり、オブジェクトの状態が変更されたときにのみ変更されます)。プロパティのように聞こえるメソッドに遭遇した開発者(Count()
、Length()
、IsOpen()
)は、何かが起こっていることを知っており、(うまくいけば)メソッドのドキュメントを読んでいますメソッドの正確な動作と動作を理解するため。
4番目の解決策があります:ドキュメントに依存。
string
クラスが不変であることをどうやって知っていますか? MSDNのドキュメントを読んだので、あなたは単にそれを知っています。一方、StringBuilder
は変更可能です。これも、ドキュメンテーションに記載されているためです。
/// <summary>
/// Represents an index of an element stored in the database.
/// </summary>
private interface IX
{
/// <summary>
/// Gets the immutable globally unique identifier of the index, the identifier being
/// assigned by the database when the index is created.
/// </summary>
Guid Id { get; }
/// <summary>
/// Gets a value indicating whether the index is invalidated, which means that the
/// indexed element is no longer valid and should be reloaded from the database. The
/// index can be invalidated by calling the <see cref="Invalidate()" /> method.
/// </summary>
bool IsInvalidated { get; }
/// <summary>
/// Invalidates the index, without forcing the indexed element to be reloaded from the
/// database.
/// </summary>
void Invalidate();
}
番目のソリューションは悪くありませんが、.NET Frameworkはそれに従いません。例えば:
StringBuilder.Length
DateTime.Now
プロパティですが、常に一定であるとは限りません。
.NET Framework全体で一貫して使用されているのは次のとおりです。
IEnumerable<T>.Count()
メソッドですが、
IList<T>.Length
プロパティです。前者の場合、何かを行うには、たとえばデータベースへのクエリなど、追加の作業が必要になる場合があります。この追加作業には時間がかかる場合があります。プロパティの場合、short時間かかることが予想されます。実際、リストの長さの間に長さが変わる可能性がありますが、実際には何も返さない。
クラスを使用すると、コードを見るだけで、オブジェクトの存続期間中にプロパティの値が変化しないことが明確に示されます。例えば、
public class Product
{
private readonly int price;
public Product(int price)
{
this.price = price;
}
public int Price
{
get
{
return this.price;
}
}
}
明確です:価格は変わりません。
悲しいことに、同じパターンをインターフェイスに適用することはできません。そのような場合、C#は十分に表現力がないため、ドキュメント(XMLドキュメントとUML図を含む)を使用してギャップを埋める必要があります。
私の2セント:
オプション3:C#以外のユーザーとしては、それほど手掛かりにはなりません。しかし、あなたが持っている引用から判断すると、これが何かを意味することは熟練したC#プログラマーには明らかです。ただし、これに関するドキュメントを追加する必要があります。
オプション2:変更される可能性のあるすべてのものにイベントを追加する予定がない限り、そこに移動しないでください。
別のオプション:
id
で、これは通常、可変オブジェクトでも不変であると想定されています。