オブジェクトの読み取り専用インスタンスを返す方法はありますか?
public class Person
{
public String FirstName { get; set; }
public String LastName { get; set; }
}
public class SomeClass
{
public SomeClass(Person manager)
{
if (manager == null)
throw new ArgumentNullException("manager");
_manager = manager;
}
private readonly Person _manager;
public Person Manager
{
get { return _manager; } //How do I make it readonly period!
}
}
Clone()を返すことでこれを行う唯一の方法は、元のクローンではなくクローンに変更が加えられるようにすることですか?配列については、配列を読み取り専用として返す関数があることを知っています。ああ、これが参照型であることはわかっています...さらに、書き込み部分をロックするための非表示のC#機能があるかどうか疑問に思っています。
Generic ReadOnlyラッパークラスを考え出そうとしましたが、高価なリフレクションなどを行わずに、プロパティを読み取り専用として取得する方法を理解できませんでした。
ああ、私は本当にすべて読み取り専用プロパティであるクラスの2番目のバージョンを作成することを避けようとしています。その時点で、クローンを返す方がよいでしょう。
余分なクラスを作成する手間を省くために、読み取り専用プロパティのみを持つインターフェイスIPersonとして実装することができます。
public interface IPerson
{
string FirstName { get; }
string LastName { get; }
}
public class Person:IPerson
{
public String FirstName { get; set; }
public String LastName { get; set; }
}
public class SomeClass
{
public SomeClass(Person manager)
{
if (manager == null)
throw new ArgumentNullException("manager");
_manager = manager;
}
private readonly Person _manager;
public IPerson Manager
{
get { return _manager; } //How do I make it readonly period!
}
}
以下のように、Personクラスを不変オブジェクトに変換できます。
public class Person
{
public Person( string firstName, string lastName )
{
FirstName = firstName;
LastName = lastName;
}
public String FirstName { get; private set; }
public String LastName { get; private set; }
}
Castle.DynamicProxy
を使用すると、特定の条件下でオブジェクトをフリーズ(不変にする)できます。詳細については、このブログを読んでください 投稿 。
そのような機能はありません-あなたはあなたのオプションをカバーしました。
クローンを作成するか、読み取り専用のPerson
型を作成します。セマンティクスがより明確であるため、通常は後者のアプローチが推奨されます。呼び出し元には、インスタンスを変更してはならない(および変更できない)ことは明らかです。
これは、List.AsReadOnlyが.NET Framework2.0でどのように実装されているかに基づいた別の例です。ブール値(IsReadOnly)は、更新を防ぐために適切なメソッドで使用されます。
public class Person
{
private string _firstName;
public string FirstName
{
get { return _firstName; }
set
{
if (!IsReadOnly) _firstName = value;
else throw new AccessViolationException("Object is read-only.");
}
}
private string _lastName;
public string LastName
{
get { return _lastName; }
set
{
if (!IsReadOnly) _lastName = value;
else throw new AccessViolationException("Object is read-only.");
}
}
internal virtual bool IsReadOnly { get { return false; } }
public ReadOnlyPerson AsReadOnly()
{
return new ReadOnlyPerson(this);
}
public class ReadOnlyPerson : Person
{
private Person _person;
internal override bool IsReadOnly { get { return true; } }
internal ReadOnlyPerson(Person person) // Contructor
{
this._person = person;
}
}
}
それをテストするには:
static void Main(string[] args)
{
Person p1 = new Person();
p1.FirstName = "Joe";
p1.LastName = "Bloe";
Console.WriteLine("First = {0} Last = {1}", p.FirstName, p.LastName);
var p2 = p1.AsReadOnly();
p2.FirstName = "Josephine"; // AccessViolationException
}
オブジェクトのすべてのプロパティをクラスの外部で読み取り専用にする方法はありません。上記の例では、Personクラス内のプロパティを読み取り専用に変更しない限り、_managerプロパティを読み取り専用にすることはできません。
Personクラスのプロパティのセッターを内部にすることができます。つまり、Personと同じアセンブリ内のクラスのみがプロパティを変更できます。
または、プロパティのセッターをプライベートにすると、Person内のコードのみがプロパティの値を変更できます。
匿名オブジェクトは読み取り専用です。