Как установить значение для свойства анонимного объекта?

это мой код, например:

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

foreach (var property in output.GetType().GetProperties())
{
    property.SetValue(output, "Test", null);
}

Возникает исключение: "Метод набора свойств не найден". Я хочу знать, как создать анонимный тип со свойствами, которые могут быть установлены.

Спасибо.

Ответы

Ответ 1

Свойства анонимного типа доступны только для чтения, и они не могут быть установлены.

Анонимные типы предоставляют удобный способ инкапсуляции набора свойства только для чтения в один объект без необходимости явно сначала определите тип. Имя типа генерируется компилятором и недоступны на уровне исходного кода. Тип каждого свойства выведенный компилятором.

Анонимные типы (Руководство по программированию на С#)

Ответ 2

Как установить значение для свойства анонимного объекта?

Поскольку мне напомнили сегодня, что ничто не является действительно неизменным при использовании рефлексии в сочетании со знаниями о том, как реализуются определенные вещи (в этом случае поддерживаются поля для свойств анонимного типа только для чтения), я счел целесообразным добавить ответ, иллюстрирующий изменение значений свойств анонимного объекта путем их сопоставления с их полями поддержки.

Этот метод основан на конкретном соглашении, используемом компилятором для обозначения этих полей поддержки: <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;
    }
}

Пример:

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 }

Более сложную версию можно найти здесь

Ответ 3

Если вы когда-нибудь сталкиваетесь с ситуацией, когда вам нужен изменяемый тип, вместо того, чтобы возиться с типом 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;
}

Ответ 4

Анонимные типы неизменяемы в С#. Я не думаю, что вы можете изменить его там.

Ответ 5

У меня был похожий сценарий, где мне нужно было присвоить код ошибки и сообщение нескольким типам объектов, что все SHARE специфичные вложенные свойства, поэтому мне не нужно дублировать мои методы ссылки, надеясь, что это поможет кому-то еще:

    public T AssignErrorMessage<T>(T response, string errorDescription, int errorCode)
    {
        PropertyInfo ErrorMessagesProperty = response.GetType().GetProperty("ErrorMessage");
        if (ErrorMessagesProperty.GetValue(response, null) == null)
            ErrorMessagesProperty.SetValue(response, new ErrorMessage());

        PropertyInfo ErrorCodeProperty = ErrorMessagesProperty.GetType().GetProperty("code");
        ErrorCodeProperty.SetValue(response, errorCode);

        PropertyInfo ErrorMessageDescription = ErrorMessagesProperty.GetType().GetProperty("description");
        ErrorMessageDescription.SetValue(response, errorDescription);

        return response;
    }

    public class ErrorMessage
    {
        public int code { get; set; }
        public string description { get; set; }
    }

Ответ 6

Предложение: вы можете сразу установить все свойства.

В других ответах правильно утверждается, что они являются неизменяемыми объектами (хотя ответ Алекс показывает, как попасть в поля поддержки, что является хорошим, но беспорядочным ответом), но у них есть открытые конструкторы, поэтому вы можете создавать новые экземпляры.

Этот пример клонирует себя для краткости, но вы можете увидеть, как конструктор может использоваться для построения объекта из определенных значений.

var anonymousType = output.GetType();
var properties = anonymousType.GetProperties();
var propertyTypes = properties.Select(p => p.PropertyType).ToArray();

//The constructor has parameters for each property on the type
var constructor = anonymousType.GetConstructor(propertyTypes);

//clone the existing values to pass to ConstructorInfo
var values = properties.Select(p => p.GetValue(output)).ToArray();
var anonymousClone = constructor.Invoke(values);

Ответ 7

Простым способом может быть сериализация анонимного объекта в Json с помощью NewtonSoft'JsonConverter (JsonConvert.SerializeObject(anonObject)). Затем вы можете изменить Json с помощью строковых манипуляций и повторно инициализировать его в новый анонимный объект, который вы можете назначить старой переменной.

Немного свернуто, но очень легко понять для новичков!