我發現自己不得不創建很多不可變類,我想找到一種方法來做到這一點,沒有多餘的信息。我不能使用匿名類型,因爲我需要從方法返回這些類。我想要智能支持,所以我不想使用字典,動態或類似的東西。我也想要有名的屬性,這排除了Tuple <>。到目前爲止,一些模式我已經試過:在C#中創建不可變類的最簡潔方法是什麼?
// 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; }
}
這是比創建只讀字段,通過構造函數設置它們,並通過屬性暴露它們的「標準」模式既有點繁瑣少。但是,這兩種方法仍然有很多冗餘的樣板。
任何想法?
你的第二種形式不是不可變的 - 你可以通過提取'Field1'來觀察變化,然後設置它,然後再次獲取它。 (除非'SetOnce'在一組之前阻止提取,這是你沒有描述過的。) – 2013-03-07 22:39:40
@JonSkeet SetOnce在set之前不會阻止提取(我會推薦這篇文章)。 – ChaseMedallion 2013-03-07 22:52:52
不幸的是,對於你提出的問題沒有解決方案。語言設計團隊非常清楚這一點。希望這個問題有一天能夠得到解決。 – 2013-03-07 22:54:21