如何设置匿名对象的属性值?

Leo*_* Vo 25 .net c# anonymous-types

这是我的代码示例:

var output = new
{
    NetSessionId = string.Empty
};

foreach (var property in output.GetType().GetProperties())
{
    property.SetValue(output, "Test", null);
}
Run Code Online (Sandbox Code Playgroud)

它发生异常:"找不到属性集方法".我想知道如何创建一个具有可设置属性的匿名类型.

谢谢.

Mar*_*zek 34

匿名类型属性是只读的,无法设置.

匿名类型提供了一种将一组只读属性封装到单个对象中的便捷方法,而无需先显式定义类型.类型名称由编译器生成,在源代码级别不可用.每个属性的类型由编译器推断.

匿名类型(C#编程指南)


Ale*_*lex 23

如何设置匿名对象的属性值?

因为我今天被提醒的是,当将反射与关于如何实现某些事物的知识相结合时,没有什么是真正不可变的(在这种情况下支持匿名类型的只读属性的字段),我认为添加说明如何解释的答案是明智的可以通过将匿名对象映射到其支持字段来更改其属性值.

此方法依赖于编译器用于命名这些支持字段的特定约定:<xxxxx>i__Field在.NET和<xxxxx>Mono中,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;
    }
}
Run Code Online (Sandbox Code Playgroud)

样品:

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);
}
Run Code Online (Sandbox Code Playgroud)

随着输出:

{ FirstField = Hello, AnotherField = 30 }
{ FirstField = Hello SO, AnotherField = 42 }
Run Code Online (Sandbox Code Playgroud)

这里可以找到更精细的版本

  • 这非常有用,谢谢!我并不完全清楚为什么匿名类型必须(在理论上)是不可变的.我试图编写一个自动存储过程调用程序,它使用匿名输入集合和匿名输出集合(并为输入和输出参数执行简单的自动C#类型到SqlDbType转换).这帮助我完成了它.(显然,还有其他方法可以做到这一点,但使用匿名类型会使调用语法非常简单,使用起来也很友好.) (2认同)
  • 由于我事先不知道属性,因此我稍微简化了它以采用属性名称而不是 lambda 表达式。我想使用 lambda 表达式的优点是在编译时自动检查存在性 - 但在您不知道编译时成员属性是什么的情况下,这也是一个缺点! (2认同)

B.K*_*.K. 5

如果您遇到过需要可变类型的情况,而不是弄乱Anonymous类型,您可以使用ExpandoObject:

示例:

var people = new List<Person>
{
    new Person { FirstName = "John", LastName = "Doe" },
    new Person { FirstName = "Jane", LastName = "Doe" },
    new Person { FirstName = "Bob", LastName = "Saget" },
    new Person { FirstName = "William", LastName = "Drag" },
    new Person { FirstName = "Richard", LastName = "Johnson" },
    new Person { FirstName = "Robert", LastName = "Frost" }
};

// Method syntax.
var query = people.Select(p =>
{
    dynamic exp = new ExpandoObject();
    exp.FirstName = p.FirstName;
    exp.LastName = p.LastName;
    return exp;
}); // or people.Select(p => GetExpandoObject(p))

// Query syntax.
var query2 = from p in people
             select GetExpandoObject(p);

foreach (dynamic person in query2) // query2 or query
{
    person.FirstName = "Changed";
    Console.WriteLine("{0} {1}", person.FirstName, person.LastName);
}

// Used with the query syntax in this example, but may also be used 
// with the method syntax just as easily.
private ExpandoObject GetExpandoObject(Person p)
{
    dynamic exp = new ExpandoObject();
    exp.FirstName = p.FirstName;
    exp.LastName = p.LastName;
    return exp;
}
Run Code Online (Sandbox Code Playgroud)

  • 在一个匿名对象中,我可以输入“ var MyVar = new {Prop1 =(string)null,Prop2 =(int?)null}”,然后Prop1和Prop2是具有已知类型但值为空值的成员。(我发现这对于将一堆参数传递给SQL存储过程调用程序很有用,您可以从C#类型中得出“ SqlDbType”类型,并且如果需要,该值仍可以为null。)在ExpandoObject中`null是null是null,不能有类型。 (2认同)
  • 我从来不喜欢告诉人们“你不应该这样做”的答案。让我们假设有时提出问题的人有充分的理由想做某事,然后告诉他们是否可以,而不是他们是否应该!(是的,99.9% 的人永远不需要这个,我同意。但也许你可以用它来做一些很酷的事情 - 比如帮助编写一个更有用/简洁/易于编码的 API,因为它使用了这个在幕后;那么我想说,如果 VB 和底层运行时让你做一些在 C# 中基本上被故意关闭的事情,那就太可惜了。) (2认同)