Uzyskaj wartość właściwości dynamicznej C # za pośrednictwem ciągu znaków

182

Chciałbym uzyskać dostęp do wartości właściwości dynamicC # za pomocą ciągu:

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

Jak mogę uzyskać wartość d.value2 („losowa”), jeśli mam tylko „wartość2” jako ciąg? W javascript mógłbym zrobić d ["value2"], aby uzyskać dostęp do wartości ("random"), ale nie jestem pewien, jak to zrobić za pomocą C # i odbicia. Najbliższe, do których doszedłem, to:

d.GetType().GetProperty("value2") ... ale nie wiem, jak uzyskać z tego rzeczywistą wartość.

Jak zawsze dziękujemy za pomoc!

TimDog
źródło
26
Zauważ, że nie jest to zamierzonym celem „dynamicznego” i że ten scenariusz nie działa lepiej z „dynamicznym” niż z „obiektem”. „dynamiczny” umożliwia dostęp do właściwości, gdy nazwa właściwości jest znana w czasie kompilacji, ale nie jest znany typ . Ponieważ w czasie kompilacji nie znasz ani nazwy, ani typu, dynamika nie pomoże ci.
Eric Lippert
Prawdopodobnie powiązane: stackoverflow.com/questions/5877251/… .
DuckMaestro
3
@EricLippert Wiem, że to pytanie jest stare, ale chcę tylko dodać komentarz na wypadek, gdyby ktoś zobaczył je w przyszłości. W niektórych przypadkach nie możesz wybrać, czy użyć dynamiki, czy obiektu (na przykład podczas korzystania z parsera JSON), a nadal możesz chcieć pobrać właściwości z ciągu znaków (na przykład z pliku konfiguracyjnego), więc to użycie nie jest tak niezwykłe jak mogłoby się początkowo wydawać.
Pedrom,

Odpowiedzi:

217

Gdy już masz swój PropertyInfo(from GetProperty), musisz zadzwonić GetValuei przekazać instancję, z której chcesz uzyskać wartość. W Twoim przypadku:

d.GetType().GetProperty("value2").GetValue(d, null);
Adam Robinson
źródło
4
Otrzymuję 'd.GetType().GetProperty("value2").GetValue(d)' threw an exception of type 'System.Reflection.TargetInvocationException' dynamic {System.Reflection.TargetInvocationException}w oknie zegarka z tym ..?
TimDog,
6
Think GetValue wymaga dodatkowego parametru - egdGetType (). GetProperty ("value2"). GetValue (d, null)
dommer
3
Czy to zadziała na prawdziwie dynamicznym ExpandoObject, a nie na typie anonimowym? Ponieważ new {}tworzy prawdziwy anonimowy typ ze zdefiniowanymi właściwościami, wywołanie GetType / GetProperty ma sens, ale co z ExpandoObject, który, jeśli wywołasz GetType, otrzymasz typ, który ma właściwości ExpandoObject, ale niekoniecznie jego właściwości dynamiczne.
Triynko
16
-1. Działa to tylko z prostymi obiektami .NET, które zostały rzutowane na dynamiczne. Nie będzie działać z żadnym niestandardowym obiektem dynamicznym, takim jak Expando lub ViewBag, używającym ASP.NET MVC
Philipp Munin
8
to działa z obiektem Expando: (((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);
}

Dodaj odniesienie do Microsoft.CSharp. Działa również w przypadku typów dynamicznych oraz prywatnych nieruchomości i pól.

Edycja : Chociaż to podejście działa, istnieje prawie 20 razy szybsza metoda z zestawu Microsoft.VisualBasic.dll :

public static object GetProperty(object target, string name)
{
    return Microsoft.VisualBasic.CompilerServices.Versioned.CallByName(target, name, CallType.Get);
}
IllidanS4 chce, aby Monica wróciła
źródło
2
Chciałem tylko wspomnieć, że wersja VisualBasic nie jest odpowiednikiem twojej oryginalnej wersji „GetProperty” (GetProperty faktycznie wywołuje dynamiczny GetMember, który działa nawet na obiektach Pythona w IronPython).
Trevor Sundberg
jaki byłby obiekt docelowy?
Demodave
@Demodave Obiekt, dla którego chcesz wywołać właściwość ( dw pytaniu).
IllidanS4 chce, aby Monica wróciła
➕1 ten pracował dla własności prywatnej, gdy zarówno FastMember i HyperDescriptor nie będzie
Chris Marisic
@ IllidanS4 Kiedy porównałeś CallSitekod z CallByNamekodem, czy porównałeś te dwa podczas buforowania CallSiteinstancji? Podejrzewam, koszt pierwszej metody jest prawie czysto aktywizacja Binderi CallSitenie wzywanieTarget()
Chris Marisic
24

Dynamitey to .net stdbiblioteka open source , która pozwala nazywać ją jak dynamicsłowo kluczowe, ale używając ciągu znaków dla nazwy właściwości zamiast kompilatora, który robi to za Ciebie, i kończy się na tym, że jest równa szybkości odbicia (co nie jest tak szybkie jak przy użyciu dynamicznego słowa kluczowego, ale jest to spowodowane dodatkowym obciążeniem buforowania dynamicznego, gdy kompilator buforuje statycznie).

Dynamic.InvokeGet(d,"value2");
jbtule
źródło
11

Najłatwiejszą metodą uzyskania zarówno a, jak setteri a getterdla właściwości, która działa dla dowolnego typu, w tym dynamici ExpandoObjectjest użycie FastMembernajszybszej metody (wykorzystuje Emit).

Możesz uzyskać wynik TypeAccessoroparty na danym typie lub na ObjectAccessorpodstawie wystąpienia określonego typu.

Przykład:

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");
MaYaN
źródło
8

W większości przypadków, gdy pytasz o obiekt dynamiczny, otrzymujesz ExpandoObject (nie w powyższym przykładzie anonimowym, ale statycznym, ale wspominasz o JavaScript, a mój wybrany parser JSON JsonFx generuje ExpandoObjects).

Jeśli twoja dynamika jest w rzeczywistości ExpandoObject, możesz uniknąć odbicia, przesyłając ją do IDictionary, jak opisano na http://msdn.microsoft.com/en-gb/library/system.dynamic.expandoobject.aspx .

Po przesłaniu do IDictionary masz dostęp do przydatnych metod, takich jak .Item i .ContainsKey

Francis Norton
źródło
Niestety, konieczność rzutowania na IDictionary i użycie na przykład TryGetValue powoduje zwrócenie zwykłego starego obiektu. W tym momencie nie można skorzystać z operatorów niejawnych, ponieważ są one brane pod uwagę tylko w czasie kompilacji. Na przykład, gdybym miał klasę Int64Proxy z niejawną konwersją na Int64 ?, to Int64? i = data.value; //data is ExpandoObjectautomatycznie wyszukałbym i wywołał niejawny operator. Z drugiej strony, gdybym musiał użyć IDictionary do sprawdzenia, czy istnieje pole „value”, dostałbym z powrotem obiekt, który nie będzie rzutowany bez błędu na Int64 ?.
Triynko
5

GetProperty / GetValue nie działa dla danych Json, zawsze generuje wyjątek zerowy, jednak możesz wypróbować następujące podejście:

Serializuj swój obiekt przy użyciu JsonConvert:

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

Następnie uzyskaj do niego bezpośredni dostęp, przesyłając go z powrotem na łańcuch:

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

Może działać bezpośrednio, stosując Convert.ToString (request) ["DynamicFieldName"], jednak nie testowałem.

Anderson
źródło
2
Ta metoda generuje błąd: błąd CS0021: nie można zastosować indeksowania z [] do wyrażenia typu „obiekt”. Użyj, new JavaScriptSerializer().Deserialize<object>(json);aby dostać się do „nieruchomości” w sposób, który sugerowałeś
Kris Kilton,
4

d.GetType (). GetProperty ("wartość2")

zwraca obiekt PropertyInfo.

Więc zrób

propertyInfo.GetValue(d)
James Gaunt
źródło
2
dzięki, to była poprawna odpowiedź, ale jak wspomniano powyżej, GetValue(d)musi byćGetValue(d,null)
TimDog,
4

Oto sposób, w jaki otrzymałem wartość właściwości 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)
        {

        }


    }
Marcelo Lima Braga
źródło
1

Aby uzyskać właściwości z dokumentu dynamicznego po .GetType()zwróceniu null, spróbuj tego:

var keyValuePairs = ((System.Collections.Generic.IDictionary<string, object>)doc);
var val = keyValuePairs["propertyName"].ToObject<YourModel>;
yzhai bruin
źródło
0

W .Net core 3.1 możesz spróbować w ten sposób

d?.value2 , d?.value3
vyeluri5
źródło
0

Podobnie jak w przypadku zaakceptowanej odpowiedzi, możesz również spróbować GetFieldzamiast GetProperty.

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

W zależności od tego, jak rzeczywistość Typezostała zaimplementowana, może to działać, gdy GetProperty () nie działa, a nawet może być szybsze.

Efreeto
źródło
FYI Difference between Property and Field in C #
3.0+