Nhận giá trị của thuộc tính động c # qua chuỗi


182

Tôi muốn truy cập giá trị của thuộc tính dynamicc # bằng một chuỗi:

dynamic d = new { value1 = "some", value2 = "random", value3 = "value" };

Làm cách nào tôi có thể nhận giá trị của d.value2 ("ngẫu nhiên") nếu tôi chỉ có "value2" dưới dạng chuỗi? Trong javascript, tôi có thể thực hiện d ["value2"] để truy cập giá trị ("ngẫu nhiên"), nhưng tôi không chắc làm thế nào để làm điều này với c # và phản chiếu. Gần nhất tôi đến là đây:

d.GetType().GetProperty("value2") ... nhưng tôi không biết làm thế nào để có được giá trị thực từ đó.

Như mọi khi, nhờ sự giúp đỡ của bạn!


26
Lưu ý rằng đây không phải là mục đích dự định của "động" và kịch bản này không hoạt động tốt hơn với "động" so với "đối tượng". "Động" cho phép truy cập các thuộc tính khi tên của thuộc tính được biết tại thời điểm biên dịch nhưng loại này thì không. Vì bạn không biết tên cũng như loại tại thời gian biên dịch, nên động sẽ không giúp bạn.
Eric Lippert


3
@EricLippert Tôi biết câu hỏi này đã cũ nhưng chỉ để bình luận trong trường hợp ai đó nhìn thấy nó trong tương lai. Trong một số trường hợp, bạn không thể chọn sử dụng động hay đối tượng (ví dụ: khi sử dụng trình phân tích cú pháp JSON) và bạn vẫn có thể muốn lấy các thuộc tính từ một chuỗi (ví dụ từ tệp cấu hình), vì vậy việc sử dụng này không phải là bất thường như người ta có thể nghĩ ban đầu.
Pedrom

Câu trả lời:


217

Khi bạn đã có PropertyInfo(từ GetProperty), bạn cần gọi GetValuevà chuyển trong trường hợp bạn muốn nhận giá trị từ đó. Trong trường hợp của bạn:

d.GetType().GetProperty("value2").GetValue(d, null);

4
Tôi đang nhận được 'd.GetType().GetProperty("value2").GetValue(d)' threw an exception of type 'System.Reflection.TargetInvocationException' dynamic {System.Reflection.TargetInvocationException}trong cửa sổ đồng hồ với điều đó ..?
TimDog

6
Hãy nghĩ GetValue cần một tham số bổ sung - egdGetType (). GetProperty ("value2"). GetValue (d, null)
dommer 8/211

3
Điều này sẽ hoạt động trên một ExpandoObject động thực sự chứ không phải là một loại ẩn danh? Vì new {}tạo ra một loại ẩn danh thực sự với các thuộc tính được xác định, gọi GetType / GetProperty có ý nghĩa, nhưng với ExpandoObject, nếu bạn gọi GetType, bạn sẽ nhận được một loại có các thuộc tính của ExpandoObject, nhưng không nhất thiết phải là thuộc tính động của nó.
Triynko

16
-1. Điều này chỉ hoạt động với các đối tượng .NET đơn giản được truyền thành động. Nó sẽ không hoạt động với bất kỳ đối tượng động tùy chỉnh nào như Expando hoặc ViewBag đã sử dụng ASP.NET MVC
Philipp Munin

8
đây là những gì hoạt động với Expando Object: (((IDadata <string, object>) x)) ["value1"]
Michael Bahig

39
public static object GetProperty(object target, string name)
{
    var site = System.Runtime.CompilerServices.CallSite<Func<System.Runtime.CompilerServices.CallSite, object, object>>.Create(Microsoft.CSharp.RuntimeBinder.Binder.GetMember(0, name, target.GetType(), new[]{Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(0,null)}));
    return site.Target(site, target);
}

Thêm tham chiếu đến Microsoft.CSharp. Hoạt động cũng cho các loại động và các thuộc tính và trường riêng.

Chỉnh sửa : Trong khi phương pháp này hoạt động, có phương pháp nhanh hơn gần 20 × từ cụm Microsoft.VisualBasic.dll :

public static object GetProperty(object target, string name)
{
    return Microsoft.VisualBasic.CompilerServices.Versioned.CallByName(target, name, CallType.Get);
}

2
Chỉ muốn đề cập rằng phiên bản VisualBasic không tương đương với phiên bản 'GetProperty' ban đầu của bạn (GetProperty thực sự gọi GetMember động, hoạt động ngay cả trên các đối tượng Python trong IronPython).
Trevor Sundberg

mục tiêu của đối tượng sẽ là gì?
Demodave

@Demodave Đối tượng mà bạn muốn gọi thuộc tính ( dtrong câu hỏi).
IllidanS4 muốn Monica trở lại vào

This1 cái này hoạt động cho các thuộc tính riêng tư khi cả FastMember và HyperDescriptor sẽ không
Chris

@ IllidanS4 khi bạn so sánh CallSitemã với CallByNamemã bạn đã so sánh hai trong khi lưu bộ đệm CallSite? Tôi nghi ngờ chi phí cho phương pháp đầu tiên của bạn gần như hoàn toàn là việc kích hoạt BinderCallSite, chứ không phải là sự mời gọi củaTarget()
Chris Marisic

24

Dynamitey là một .net stdthư viện mã nguồn mở , cho phép bạn gọi nó như dynamictừ khóa, nhưng sử dụng một chuỗi cho tên thuộc tính thay vì trình biên dịch làm điều đó cho bạn, và nó kết thúc bằng phản xạ theo tốc độ (không nhanh như vậy như sử dụng từ khóa động, nhưng điều này là do chi phí phụ của bộ nhớ đệm động, trong đó trình biên dịch lưu trữ tĩnh).

Dynamic.InvokeGet(d,"value2");

11

Phương pháp đơn giản nhất để có được cả a settervà a gettercho một thuộc tính hoạt động cho bất kỳ loại nào bao gồm dynamicExpandoObjectlà sử dụng FastMembercũng là phương pháp nhanh nhất xung quanh (nó sử dụng Emit).

Bạn có thể lấy một loại TypeAccessordựa trên một loại nhất định hoặc ObjectAccessordựa trên một thể hiện của một loại nhất định.

Thí dụ:

var staticData = new Test { Id = 1, Name = "France" };
var objAccessor = ObjectAccessor.Create(staticData);
objAccessor["Id"].Should().Be(1);
objAccessor["Name"].Should().Be("France");

var anonymous = new { Id = 2, Name = "Hilton" };
objAccessor = ObjectAccessor.Create(anonymous);
objAccessor["Id"].Should().Be(2);
objAccessor["Name"].Should().Be("Hilton");

dynamic expando = new ExpandoObject();
expando.Id = 3;
expando.Name = "Monica";
objAccessor = ObjectAccessor.Create(expando);
objAccessor["Id"].Should().Be(3);
objAccessor["Name"].Should().Be("Monica");

var typeAccessor = TypeAccessor.Create(staticData.GetType());
typeAccessor[staticData, "Id"].Should().Be(1);
typeAccessor[staticData, "Name"].Should().Be("France");

typeAccessor = TypeAccessor.Create(anonymous.GetType());
typeAccessor[anonymous, "Id"].Should().Be(2);
typeAccessor[anonymous, "Name"].Should().Be("Hilton");

typeAccessor = TypeAccessor.Create(expando.GetType());
((int)typeAccessor[expando, "Id"]).Should().Be(3);
((string)typeAccessor[expando, "Name"]).Should().Be("Monica");

8

Phần lớn thời gian khi bạn yêu cầu một đối tượng động, bạn sẽ nhận được một ExpandoObject (không phải trong ví dụ ẩn danh nhưng được đánh dấu tĩnh ở trên câu hỏi, nhưng bạn đề cập đến JavaScript và trình phân tích cú pháp JSON được chọn của tôi, đối với một, tạo ra ExpandoObjects).

Nếu động của bạn trên thực tế là ExpandoObject, bạn có thể tránh phản xạ bằng cách chuyển nó sang IDadata, như được mô tả tại http://msdn.microsoft.com/en-gb/l Library / system.dynamic.Exandoobject.aspx .

Khi bạn đã chuyển sang IDadata, bạn có quyền truy cập vào các phương thức hữu ích như .Item và .ContainsKey


Thật không may, chẳng hạn như phải chuyển sang IDadata và sử dụng TryGetValue, dẫn đến một đối tượng cũ đơn giản được trả về. Bạn không thể tận dụng các toán tử ẩn tại thời điểm đó, vì chúng chỉ được xem xét tại thời điểm biên dịch. Ví dụ: nếu tôi có một lớp Int64Proxy với chuyển đổi ngầm định thành Int64?, Thì Int64? i = data.value; //data is ExpandoObjectsẽ tự động tra cứu và gọi toán tử ẩn. Mặt khác, nếu tôi phải sử dụng IDadata để kiểm tra xem trường "giá trị" có tồn tại hay không, tôi sẽ lấy lại một đối tượng sẽ không truyền mà không có lỗi với Int64?.
Triynko

5

GetProperty / GetValue không hoạt động đối với dữ liệu Json, nó luôn tạo ra một ngoại lệ null, tuy nhiên, bạn có thể thử phương pháp này:

Tuần tự hóa đối tượng của bạn bằng JsonConvert:

var z = Newtonsoft.Json.JsonConvert.DeserializeObject(Convert.ToString(request));

Sau đó truy cập trực tiếp, chuyển nó trở lại chuỗi:

var pn = (string)z["DynamicFieldName"];

Nó có thể hoạt động trực tiếp khi áp dụng Convert.ToString (request) ["DynamicFieldName"], tuy nhiên tôi chưa thử nghiệm.


2
Phương thức này tạo ra lỗi: error CS0021: Không thể áp dụng lập chỉ mục với [] cho một biểu thức của loại 'đối tượng'. Sử dụng new JavaScriptSerializer().Deserialize<object>(json);để đi đến "tài sản" theo cách bạn đề xuất
Kris Kilton

4

d.GetType (). GetProperty ("value2")

trả về một đối tượng PropertyInfo.

Vậy thì làm

propertyInfo.GetValue(d)

2
cảm ơn, đây là câu trả lời đúng, nhưng như đã đề cập ở trên, GetValue(d)cần phải cóGetValue(d,null)
TimDog

4

Đây là cách tôi đã có giá trị của một giá trị tài sản của một dinamic:

    public dynamic Post(dynamic value)
    {            
        try
        {
            if (value != null)
            {
                var valorCampos = "";

                foreach (Newtonsoft.Json.Linq.JProperty item in value)
                {
                    if (item.Name == "valorCampo")//property name
                        valorCampos = item.Value.ToString();
                }                                           

            }
        }
        catch (Exception ex)
        {

        }


    }

1

Để có được các thuộc tính từ tài liệu động khi .GetType()trả về null, hãy thử điều này:

var keyValuePairs = ((System.Collections.Generic.IDictionary<string, object>)doc);
var val = keyValuePairs["propertyName"].ToObject<YourModel>;

0

Trong .Net core 3.1, bạn có thể thử như thế này

d?.value2 , d?.value3

0

Tương tự như câu trả lời được chấp nhận, bạn cũng có thể thử GetFieldthay vì GetProperty.

d.GetType().GetField("value2").GetValue(d);

Tùy thuộc vào cách thực tế Typeđược triển khai, điều này có thể hoạt động khi GetProperty () không và thậm chí có thể nhanh hơn.


Sự khác biệt của FYI giữa Tài sản và Trường trong C # 3.0+: stackoverflow.com/a/653799/2680660
Efreeto
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.