如何修改 匿名类型 中的属性值 ?

Posted dotNET跨平台

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何修改 匿名类型 中的属性值 ?相关的知识,希望对你有一定的参考价值。

咨询区

  • 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 

点评区

这个题目其实很有意思,虽然语言和框架设计者用了各种限制来阻止我们做一些事情,其实都有化解的方法,所以并没有永恒的不可变,最彻底的还可以通过修改内存地址变更,不是嘛~

以上是关于如何修改 匿名类型 中的属性值 ?的主要内容,如果未能解决你的问题,请参考以下文章

定义类+类实例化+属性+构造函数+匿名类型var+堆与栈+GC回收机制+值类型与引用类型

具有匿名类型的 C# LINQ 构建表达式

如何检查 C# 中的动态匿名类型上是不是存在属性?

在 TypeScript 中键入匿名对象的属性

使用'class'(或其他保留关键字)作为匿名类型的属性

如何使用 lambda 表达式和匿名类型获取类型的属性名称?