如何延迟计算C#匿名类的属性值?
如何让匿名类型的属性延迟到后续计算?
我希望定义一个匿名类型,让它的属性值不在创建时计算,而是等到后续完成某些处理后再计算。举个例子:
private int _num = 1; private int GetNum() => _num; private void DoSomeWork() => _num = 100; private void Foo() { // 这段代码无法运行,仅用于表达需求 var obj = new { Number => GetNum(); }; DoSomeWork(); Serialize(obj); // 期望序列化结果是 {"Number": 100} }
这段代码会触发错误:
CS0828: Cannot assign 'anonymous method' to anonymous type property.
有没有解决办法?
补充:我清楚可以改成下面这样,但并不希望这么做:
DoSomeWork(); Serialize(new { Number = GetNum() });
可行的解决方法
方法1:将属性定义为委托类型
直接把匿名类型的属性设为Func<int>,创建时不会执行GetNum(),等到需要取值时再调用委托:
private void Foo() { var obj = new { Number = (Func<int>)GetNum }; DoSomeWork(); // 序列化前手动调用委托获取值,或让序列化工具支持解析委托 var valueToSerialize = new { Number = obj.Number() }; Serialize(valueToSerialize); }
如果使用的序列化工具支持自定义逻辑(比如Newtonsoft.Json),可以编写转换器自动解析Func<T>类型的属性,无需手动二次包装。
方法2:使用自定义延迟包装类
定义一个通用的延迟取值类,把计算逻辑封装进去,再配置序列化工具读取它的实际值:
public class LazyValue<T> { private readonly Func<T> _calculation; public LazyValue(Func<T> calculation) => _calculation = calculation; public T Value => _calculation(); } // 使用方式 private void Foo() { var obj = new { Number = new LazyValue<int>(GetNum) }; DoSomeWork(); Serialize(obj); }
以Newtonsoft.Json为例,需要给LazyValue<T>编写自定义转换器,让序列化时自动读取Value属性:
public class LazyValueConverter : JsonConverter { public override bool CanConvert(Type objectType) { return objectType.IsGenericType && objectType.GetGenericTypeDefinition() == typeof(LazyValue<>); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { throw new NotImplementedException("反序列化逻辑按需实现"); } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { var valueProperty = value.GetType().GetProperty("Value"); var actualValue = valueProperty.GetValue(value); serializer.Serialize(writer, actualValue); } }
序列化时注册该转换器,即可得到期望的结果。
方法3:使用ExpandoObject(动态类型)
如果可以接受动态类型,用ExpandoObject也能实现延迟计算:
private void Foo() { dynamic obj = new ExpandoObject(); // 用属性访问器包装延迟逻辑 ((IDictionary<string, object>)obj)["Number"] = new Func<int>(GetNum); DoSomeWork(); // 序列化前转换为实际值,或配置工具处理动态类型 var serializedObj = new { Number = ((Func<int>)obj.Number)() }; Serialize(serializedObj); }
这种方式灵活性高,但类型安全性差,适合简单场景。
内容的提问来源于stack exchange,提问作者Cristi
相关产品推荐
相关产品推荐

