咨询区
Leo Vo
我有下面一段代码:
var output = new
{NetSessionId = string.Empty
};foreach (var property in output.GetType().GetProperties())
{property.SetValue(output, "Test", null);
}
代码运行后,它会抛出如下异常:
Property set method not found
我想知道如何给这个 匿名类型
的属性赋值?
回答区
Alex
从 MSDN :https://docs.microsoft.com/en-us/dotnet/csharp/fundamentals/types/anonymous-types 的描述来看,理论上 匿名类型
是不可变的,一旦定义好之后,你是无法对它重新赋值。
但我想提醒的是,其实并没有所谓永恒的不可变,你要是真想变,肯定是有办法的,比如下面的 匿名类
。
var myAnonInstance = new{FirstField = "Hello",AnotherField = 30,};
当你用 ILSpy 反编译后代码如下:
internal sealed class <>f__AnonymousType0<<FirstField>j__TPar, <AnotherField>j__TPar>{[DebuggerBrowsable(DebuggerBrowsableState.Never)]private readonly <FirstField>j__TPar<FirstField> i__Field;[DebuggerBrowsable(DebuggerBrowsableState.Never)]private readonly <AnotherField>j__TPar<AnotherField> i__Field;public <FirstField>j__TPar FirstField{get{return < FirstField > i__Field;}}public <AnotherField>j__TPar AnotherField{get{return < AnotherField > i__Field;}}}
可以看到,底层的字段
其实是有默认规范的: <xxxxx>i__Field
, 这里的 xxxxx 就是属性名字,接下来就可以用 反射
来修改背后的字段
即可,参考代码如下:
public static class AnonymousObjectMutator
{private const BindingFlags FieldFlags = BindingFlags.NonPublic | BindingFlags.Instance;private static readonly string[] BackingFieldFormats = { "<{0}>i__Field", "<{0}>" };public static T Set<T, TProperty>(this T instance,Expression<Func<T, TProperty>> propExpression,TProperty newValue) where T : class{var pi = (propExpression.Body as MemberExpression).Member;var backingFieldNames = BackingFieldFormats.Select(x => string.Format(x, pi.Name)).ToList();var fi = typeof(T).GetFields(FieldFlags).FirstOrDefault(f => backingFieldNames.Contains(f.Name));if (fi == null)throw new NotSupportedException(string.Format("Cannot find backing field for {0}", pi.Name));fi.SetValue(instance, newValue);return instance;}
}
然后你可以这样使用。
public static void Main(params string[] args)
{var myAnonInstance = new { FirstField = "Hello", AnotherField = 30, };Console.WriteLine(myAnonInstance);myAnonInstance.Set(x => x.FirstField, "Hello SO").Set(x => x.AnotherField, 42);Console.WriteLine(myAnonInstance);
}
输出结果:
{ FirstField = Hello, AnotherField = 30 }
{ FirstField = Hello SO, AnotherField = 42 }
点评区
这个题目其实很有意思,虽然语言和框架设计者用了各种限制来阻止我们做一些事情,其实都有化解的方法,所以并没有永恒的不可变,最彻底的还可以通过修改内存地址变更,不是嘛~