2013-07-03 44 views
17

這是我的示例代碼:如何設置匿名對象的屬性值?

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

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

過程中出現異常:「沒有找到屬性設置方法」。我想知道如何創建一個可以設置屬性的匿名類型。

謝謝。

+0

[可能使用反射/ TypeDescriptor設置匿名類型的屬性值是否可能?](http://stackoverflow.com/questions/2765367/set-value-of-a-property-of- an-anonymous-type-using-reflection-typedescriptor-i) –

+0

我建議你看一下[ExpandoObject Class](http://msdn.microsoft.com/en-us/library/system.dynamic.expando對象。 ASPX)。 –

+0

正如@AlexFilipovici指出的那樣,您可以通過更改其支持字段的值來更改匿名對象上的屬性值。這是一種脆弱的方法,它依賴於如何命名這些支持字段以建立屬性和相應的支持字段之間的關係的知識。我只是添加了一個答案來說明如何做到這一點。 – Alex

回答

20

匿名類型屬性是隻讀的,它們不能設置。

匿名類型提供一種方便的方式來封裝一組 只讀屬性成一個單一的對象,而不必明確 第一個定義一個類型。類型名稱由編譯器生成,並且 在源代碼級別不可用。每個屬性的類型是由編譯器推斷的 。

Anonymous Types (C# Programming Guide)

+0

請參閱: http://stackoverflow.com/questions/2328676/anonymous-type-property-setters – icbytes

+0

最好的答案。謝謝。 –

1

匿名類型是在C#不可變的。我不認爲你可以改變那裏的財產。

14

如何設置匿名對象的屬性值?

因爲我今天想起,沒有什麼與知識的結合使用反射時是真正一成不變如何某些事情實現(支持字段在這種情況下,匿名類型的只讀屬性),我認爲明智的做法添加一個答案,說明匿名對象的屬性值如何通過將它們映射到其後備字段來更改。

此方法依賴編譯器用於命名這些支持字段的特定約定:.NET中的<xxxxx>i__Field和Mono中的<xxxxx>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 } 

稍微更復雜的版本可以發現here

+1

這非常有用,謝謝!我不完全清楚爲什麼匿名類型必須(理論上)是不可變的。我正在嘗試編寫一個自動存儲過程調用程序,它使用匿名輸入集合和匿名輸出集合(並對輸入和輸出參數執行SqlDbType轉換的簡單自動C#類型)。這幫助我完成了它。 (顯然,還有其他方法可以做到這一點,但使用匿名類型會使調用語法非常簡單並且易於使用。) –

+0

由於我事先不知道屬性,所以稍微簡化了此操作以獲取屬性名稱而不是一個lambda表達式。我猜想使用lambda表達式的優點是在編譯時自動存在檢查 - 但在編譯時您不知道成員屬性會是什麼的上下文中也是一個缺點! –

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; 
} 
+0

我認爲這不適用於你需要輸入空值的情況? (即'ExpandoObject'不能支持,但匿名類型可以) –

+0

@bmju不確定你的意思。你必須舉個例子。 –

+1

在一個匿名對象中,我可以把'var MyVar = new {Prop1 =(string)null,Prop2 =(int?)null}',然後Prop1和Prop2成爲具有已知類型但爲空值的成員。 (我發現這對於將一堆參數傳遞給SQL存儲過程調用者很有用,可以從C#類型中找出'SqlDbType'類型,如果需要,值仍然可以爲null。)在一個'ExpandoObject 'null是一個null是一個null,它不能有一個類型。 –

0

我其中我需要分配一個錯誤代碼和消息到所有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; } 
    } 
0

建議:您可以一次設置所有屬性。

其他答案正確地表明它們是不可變的對象(雖然Alex的答案確實顯示瞭如何獲得後備字段,這是一個很好的,但凌亂的答案),但他們確實暴露了構造函數,因此您可以創建新實例。

爲了簡潔起見,本示例克隆了自身,但您可以看到如何使用構造函數從定義的值構建對象。

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); 
0

一個簡單的方法可能是序列化匿名對象以JSON與NewtonSoft'JsonConverter(JsonConvert.SerializeObject(anonObject))。然後,您可以通過字符串操作來更改Json,並將其重新串入一個新的匿名對象,您可以將其分配給舊變量。

有點複雜但對初學者來說很容易理解!