รับค่าของคุณสมบัติ c # ไดนามิกผ่านทางสตริง


182

ฉันต้องการเข้าถึงค่าของdynamicคุณสมบัติ c # ด้วยสตริง:

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

ฉันจะรับค่าของ d.value2 ("random") ได้อย่างไรถ้าฉันมี "value2" เป็นสตริงเท่านั้น? ในจาวาสคริปต์ฉันสามารถทำ d ["value2"] เพื่อเข้าถึงค่า ("สุ่ม") แต่ฉันไม่แน่ใจว่าจะทำอย่างไรกับ c # และการสะท้อนกลับ สิ่งที่ฉันใกล้ที่สุดคือ:

d.GetType().GetProperty("value2") ... แต่ฉันไม่รู้ว่าจะได้รับมูลค่าที่แท้จริงได้อย่างไร

และเช่นเคยขอขอบคุณสำหรับความช่วยเหลือของคุณ!


26
โปรดทราบว่านี่ไม่ใช่จุดประสงค์ของ "ไดนามิก" และสถานการณ์นี้ไม่ทำงานได้ดีกว่ากับ "ไดนามิก" มากกว่า "วัตถุ" "ไดนามิก" ทำให้สามารถเข้าถึงคุณสมบัติได้เมื่อทราบชื่อของทรัพย์สินในเวลารวบรวม แต่ชนิดไม่ เมื่อคุณรู้ทั้งชื่อและชนิดของเวลาการคอมไพล์ไดนามิกจะไม่ช่วยคุณ
Eric Lippert

อาจเกี่ยวข้องกับ: stackoverflow.com/questions/5877251/
DuckMaestro

3
@EricLippert ฉันรู้ว่าคำถามนี้เก่า แต่เพียงแค่ให้ความเห็นในกรณีที่มีคนเห็นมันในอนาคต ในบางกรณีคุณไม่สามารถเลือกได้ว่าจะใช้ไดนามิกหรือวัตถุ (ตัวอย่างเช่นเมื่อใช้ตัวแยกวิเคราะห์ JSON) และคุณยังอาจต้องการรับคุณสมบัติจากสตริง (จากไฟล์กำหนดค่าเป็นต้น) ดังนั้นการใช้งานจึงไม่แปลก อย่างที่ใคร ๆ ก็คิดกัน
Pedrom

คำตอบ:


217

เมื่อคุณมีPropertyInfo(จากGetProperty) คุณจะต้องโทรGetValueและส่งอินสแตนซ์ที่คุณต้องการรับค่า ในกรณีของคุณ:

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

4
ฉันกำลัง'd.GetType().GetProperty("value2").GetValue(d)' threw an exception of type 'System.Reflection.TargetInvocationException' dynamic {System.Reflection.TargetInvocationException}เข้าไปในหน้าต่างดูด้วย ..
TimDog

6
คิดว่า GetValue ต้องการพารามิเตอร์เพิ่มเติมเช่น egdGetType () GetProperty ("value2") GetValue (d, null)
dommer

3
สิ่งนี้จะใช้ได้กับ ExpandoObject จริงแบบไดนามิกมากกว่าเป็นแบบไม่ระบุชื่อหรือไม่? ตั้งแต่new {}สร้างชนิดไม่ระบุชื่อจริงพร้อมด้วยคุณสมบัติที่กำหนดการโทร GetType / GetProperty สมเหตุสมผล แต่สิ่งที่เกี่ยวกับ ExpandoObject ซึ่งถ้าคุณโทร GetType คุณจะได้รับประเภทที่มีคุณสมบัติของ ExpandoObject แต่ไม่จำเป็นต้องเป็นคุณสมบัติแบบไดนามิก
Triynko

16
-1 ใช้งานได้เฉพาะกับวัตถุ. NET แบบธรรมดาที่ถูกใช้เป็นแบบไดนามิก มันจะไม่ทำงานกับวัตถุแบบไดนามิกที่กำหนดเองใด ๆ เช่น Expando หรือ ViewBag ใช้ ASP.NET MVC
ฟิลิปป์ Munin

8
นี่คือสิ่งที่ทำงานกับ Expando Object: (((IDictionary <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);
}

เพิ่มการอ้างอิงถึง Microsoft.CSharp ใช้งานได้กับประเภทไดนามิกและคุณสมบัติส่วนตัวและฟิลด์

แก้ไข : ในขณะที่วิธีนี้ใช้งานได้มีวิธีการเร็วขึ้นเกือบ 20 เท่าจากชุดประกอบMicrosoft.VisualBasic.dll :

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

2
แค่อยากจะพูดถึงว่ารุ่น VisualBasic ไม่เท่ากับรุ่น 'GetProperty' ดั้งเดิมของคุณ (GetProperty จะเรียกใช้ GetMember แบบไดนามิกซึ่งทำงานได้แม้บนวัตถุ Python ใน IronPython)
เทรเวอร์ Sundberg

เป้าหมายวัตถุจะเป็นอย่างไร
Demodave

@Demodave วัตถุที่คุณต้องการเรียกใช้คุณสมบัติ ( dในคำถาม)
IllidanS4 ต้องการโมนิก้ากลับเมื่อ

➕1ใช้งานได้สำหรับคุณสมบัติส่วนตัวเมื่อทั้ง FastMember และ HyperDescriptor จะไม่ทำเช่นนี้
Chris Marisic

@ IllidanS4 เมื่อคุณเปรียบเทียบCallSiteโค้ดกับCallByNameโค้ดคุณเปรียบเทียบทั้งสองขณะที่แคชCallSiteอินสแตนซ์หรือไม่ ฉันสงสัยว่าค่าใช้จ่ายของวิธีแรกของคุณเกือบจะเป็นการเปิดใช้งานอย่างBinderCallSiteTarget()
แท้จริง

24

Dynamiteyเป็นโอเพ่นซอร์สไลบรารี่.net stdที่ให้คุณเรียกมันว่าdynamicคีย์เวิร์ด แต่ใช้สตริงสำหรับชื่อคุณสมบัติมากกว่าคอมไพเลอร์ที่ทำเพื่อคุณและมันก็กลายเป็นเท่ากับการสะท้อนตามสปีด เป็นการใช้คำสำคัญแบบไดนามิก แต่เป็นเพราะส่วนเกินพิเศษของการแคชแบบไดนามิกที่คอมไพเลอร์แคชแบบคงที่)

Dynamic.InvokeGet(d,"value2");

11

วิธีที่ง่ายที่สุดในการรับทั้ง a setterและ a getterสำหรับคุณสมบัติที่ใช้งานได้กับทุกประเภทรวมถึงdynamicและExpandoObjectคือการใช้FastMemberซึ่งก็เป็นวิธีที่เร็วที่สุดด้วย (มันใช้ Emit)

คุณสามารถได้รับTypeAccessorตามประเภทที่กำหนดหรือObjectAccessorตามตัวอย่างของประเภทที่กำหนด

ตัวอย่าง:

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

เวลาส่วนใหญ่เมื่อคุณถามหาวัตถุแบบไดนามิกคุณจะได้รับ ExpandoObject (ไม่ใช่ในตัวอย่างที่ไม่ระบุชื่อ แต่เป็นแบบสแตติกแบบคงที่ด้านบน แต่คุณพูดถึง JavaScript และตัวแยกวิเคราะห์ JSON JsonFx ของฉันที่เลือกไว้

ถ้าแบบไดนามิกของคุณในความเป็นจริง ExpandoObject คุณสามารถหลีกเลี่ยงการสะท้อนโดยการหล่อมัน IDictionary ตามที่อธิบายไว้ในhttp://msdn.microsoft.com/en-gb/library/system.dynamic.expandoobject.aspx

เมื่อคุณส่งไปที่ IDictionary คุณจะสามารถเข้าถึงวิธีการที่มีประโยชน์เช่น. Item และ .ContainKey


น่าเสียดายที่ต้องใช้ IDictionary และใช้ TryGetValue ส่งผลให้วัตถุเก่าธรรมดาถูกส่งคืน คุณไม่สามารถใช้ประโยชน์จากโอเปอเรเตอร์โดยนัย ณ จุดนั้นเนื่องจากมีการพิจารณาในเวลารวบรวมเท่านั้น ตัวอย่างเช่นถ้าฉันมีคลาส Int64Proxy ที่มีการแปลงโดยนัยเป็น Int64 แล้วInt64? i = data.value; //data is ExpandoObjectจะค้นหาและเรียกโอเปอเรเตอร์โดยอัตโนมัติ ในทางกลับกันถ้าฉันต้องใช้ IDictionary เพื่อทดสอบว่ามีช่อง "ค่า" อยู่หรือไม่ฉันจะได้รับวัตถุคืนที่จะไม่ส่งสัญญาณโดยไม่มีข้อผิดพลาดกับ Int64 หรือไม่
Triynko

5

GetProperty / GetValue ไม่ทำงานสำหรับข้อมูล Json มันจะสร้างข้อยกเว้นเป็นโมฆะเสมออย่างไรก็ตามคุณสามารถลองใช้วิธีนี้:

ทำให้เป็นอันดับวัตถุของคุณโดยใช้ JsonConvert:

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

จากนั้นเข้าถึงโดยตรงหล่อมันกลับไปที่สตริง:

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

มันอาจจะทำงานได้โดยตรงโดยใช้ Convert.ToString (ขอ) ["DynamicFieldName"] แต่ฉันยังไม่ได้ทดสอบ


2
วิธีนี้สร้างข้อผิดพลาด: ข้อผิดพลาด CS0021: ไม่สามารถใช้การจัดทำดัชนีด้วย [] กับการแสดงออกประเภท 'วัตถุ' ใช้new JavaScriptSerializer().Deserialize<object>(json);เพื่อไปที่ "คุณสมบัติ" ตามที่คุณแนะนำ
Kris Kilton

4

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

ส่งคืนวัตถุ PropertyInfo

ถ้าอย่างนั้นทำ

propertyInfo.GetValue(d)

2
ขอบคุณนี่เป็นคำตอบที่ถูกต้อง แต่ตามที่กล่าวไว้ข้างต้นGetValue(d)ความต้องการที่จะเป็นGetValue(d,null)
TimDog

4

นี่คือวิธีที่ฉันได้รับมูลค่าของมูลค่าทรัพย์สินของไดนามิค:

    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

ในการรับคุณสมบัติจาก doc แบบไดนามิกเมื่อ.GetType()ส่งคืนให้nullลองสิ่งนี้:

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


0

เช่นเดียวกับคำตอบที่ยอมรับคุณสามารถลองGetFieldแทนGetPropertyได้

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

ทั้งนี้ขึ้นอยู่กับวิธีการที่เกิดขึ้นจริงTypeได้ดำเนินการนี้อาจทำงานเมื่อ GetProperty () ไม่สามารถแม้แต่จะเร็วขึ้น


FYI ความแตกต่างระหว่างคุณสมบัติและฟิลด์ใน C # 3.0+: stackoverflow.com/a/653799/2680660
Efreeto
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.