Tôi thấy mình phải tạo ra rất nhiều lớp học bất biến và tôi muốn tìm cách để làm điều đó mà không cần thông tin dư thừa. Tôi không thể sử dụng một kiểu ẩn danh vì tôi cần trả về các lớp này từ các phương thức. Tôi muốn hỗ trợ intellisense, vì vậy tôi không muốn sử dụng từ điển, năng động hoặc bất cứ điều gì như thế. Tôi cũng muốn các thuộc tính được đặt tên tốt, quy định ra Tuple <>. Cho đến nay, một số mô hình tôi đã cố gắng:Cách súc tích nhất để tạo ra một lớp không thay đổi trong C# là gì?
// inherit Tuple<>. This has the added benefit of giving you Equals() and GetHashCode()
public class MyImmutable : Tuple<int, string, bool> {
public MyImmutable(int field1, string field2, bool field3) : base(field1, field2, field3) { }
public int Field1 { get { return this.Item1; } }
public string Field2 { get { return this.Item2; } }
public bool Field3 { get { return this.Item3; } }
}
///////////////////////////////////////////////////////////////////////////////////
// using a custom SetOnce<T> struct that throws an error if set twice or if read before being set
// the nice thing about this approach is that you can skip writing a constructor and
// use object initializer syntax.
public class MyImmutable {
private SetOnce<int> _field1;
private SetOnce<string> _field2;
private SetOnce<bool> _field3;
public int Field1 { get { return this._field1.Value; } set { this._field1.Value = value; }
public string Field2 { get { return this._field2.Value; } set { this._field2.Value = value; }
public bool Field3 { get { return this._field3.Value; } set { this._field3.Value = value; }
}
///////////////////////////////////////////////////////////////////////////////////
// EDIT: another idea I thought of: create an Immutable<T> type which allows you to
// easily expose types with simple get/set properties as immutable
public class Immutable<T> {
private readonly Dictionary<PropertyInfo, object> _values;
public Immutable(T obj) {
// if we are worried about the performance of this reflection, we could always statically cache
// the getters as compiled delegates
this._values = typeof(T).GetProperties()
.Where(pi => pi.CanRead)
// Utils.MemberComparer is a static IEqualityComparer that correctly compares
// members so that ReflectedType is ignored
.ToDictionary(pi => pi, pi => pi.GetValue(obj, null), Utils.MemberComparer);
}
public TProperty Get<TProperty>(Expression<Func<T, TProperty>> propertyAccessor) {
var prop = (PropertyInfo)((MemberExpression)propertyAccessor.Body).Member;
return (TProperty)this._values[prop];
}
}
// usage
public class Mutable { int A { get; set; } }
// we could easily write a ToImmutable extension that would give us type inference
var immutable = new Immutable<Mutable>(new Mutable { A = 5 });
var a = immutable.Get(m => m.A);
// obviously, this is less performant than the other suggestions and somewhat clumsier to use.
// However, it does make declaring the immutable type quite concise, and has the advantage that we can make
// any mutable type immutable
///////////////////////////////////////////////////////////////////////////////////
// EDIT: Phil Patterson and others mentioned the following pattern
// this seems to be roughly the same # characters as with Tuple<>, but results in many
// more lines and doesn't give you free Equals() and GetHashCode()
public class MyImmutable
{
public MyImmutable(int field1, string field2, bool field3)
{
Field1 = field1;
Field2 = field2;
Field3 = field3;
}
public int Field1 { get; private set; }
public string Field2 { get; private set; }
public bool Field3 { get; private set; }
}
Cả hai đều là hơi ít tiết hơn so với mô hình "chuẩn" của việc tạo ra các lĩnh vực readonly, đặt chúng thông qua một nhà xây dựng, và phơi bày chúng thông qua thuộc tính. Tuy nhiên, cả hai phương pháp này vẫn có nhiều bản mẫu dự phòng.
Bất kỳ ý tưởng nào?
Biểu mẫu thứ hai của bạn không thay đổi - bạn có thể quan sát thay đổi bằng cách tìm nạp 'Field1', sau đó đặt nó, sau đó tìm nạp lại. (Trừ khi 'SetOnce' ngăn cản tìm nạp trước một tập hợp, mà bạn chưa mô tả.) –
@JonSkeet SetOnce ngăn chặn tìm nạp trước khi được thiết lập (Tôi sẽ ghi lại bài đăng). – ChaseMedallion
Thật không may là không có giải pháp slam-dunk cho vấn đề bạn đặt ra. Nhóm thiết kế ngôn ngữ nhận thức rõ điều đó. Hy vọng rằng một ngày nào đó sẽ được giải quyết. –