Dynamicznie dodawaj właściwości języka C # w czasie wykonywania

89

Wiem, że jest kilka pytań, które to rozwiązują, ale odpowiedzi zwykle idą w ślad za zaleceniem słownika lub zbioru parametrów, co nie działa w mojej sytuacji.

Korzystam z biblioteki, która działa poprzez refleksję, aby robić wiele sprytnych rzeczy z obiektami o właściwościach. Działa to ze zdefiniowanymi klasami, a także klasami dynamicznymi. Muszę pójść o krok dalej i zrobić coś w ten sposób:

public static object GetDynamicObject(Dictionary<string,object> properties) {
    var myObject = new object();
    foreach (var property in properties) {
        //This next line obviously doesn't work... 
        myObject.AddProperty(property.Key,property.Value);
    }
    return myObject;
}

public void Main() {
    var properties = new Dictionary<string,object>();
    properties.Add("Property1",aCustomClassInstance);
    properties.Add("Property2","TestString2");

    var myObject = GetDynamicObject(properties);

    //Then use them like this (or rather the plug in uses them through reflection)
    var customClass = myObject.Property1;
    var myString = myObject.Property2;

}

Biblioteka działa dobrze z dynamicznymi typami zmiennych, z właściwościami przypisywanymi ręcznie. Jednak nie wiem, ile lub jakie właściwości zostaną wcześniej dodane.

Paul Grimshaw
źródło

Odpowiedzi:

105

Czy przyjrzałeś się ExpandoObject?

patrz: http://blogs.msdn.com/b/csharpfaq/archive/2009/10/01/dynamic-in-c-4-0-introducing-the-expandoobject.aspx

Z MSDN:

Klasa ExpandoObject umożliwia dodawanie i usuwanie członków jej instancji w czasie wykonywania, a także ustawianie i pobieranie wartości tych elementów. Ta klasa obsługuje dynamiczne wiązanie, które umożliwia użycie standardowej składni, takiej jak sampleObject.sampleMember, zamiast bardziej złożonej składni, takiej jak sampleObject.GetAttribute („sampleMember”).

Umożliwiając robienie fajnych rzeczy, takich jak:

dynamic dynObject = new ExpandoObject();
dynObject.SomeDynamicProperty = "Hello!";
dynObject.SomeDynamicAction = (msg) =>
    {
        Console.WriteLine(msg);
    };

dynObject.SomeDynamicAction(dynObject.SomeDynamicProperty);

W oparciu o rzeczywisty kod, który może Cię bardziej zainteresować:

public static dynamic GetDynamicObject(Dictionary<string, object> properties)
{
    return new MyDynObject(properties);
}

public sealed class MyDynObject : DynamicObject
{
    private readonly Dictionary<string, object> _properties;

    public MyDynObject(Dictionary<string, object> properties)
    {
        _properties = properties;
    }

    public override IEnumerable<string> GetDynamicMemberNames()
    {
        return _properties.Keys;
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        if (_properties.ContainsKey(binder.Name))
        {
            result = _properties[binder.Name];
            return true;
        }
        else
        {
            result = null;
            return false;
        }
    }

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        if (_properties.ContainsKey(binder.Name))
        {
            _properties[binder.Name] = value;
            return true;
        }
        else
        {
            return false;
        }
    }
}

W ten sposób potrzebujesz tylko:

var dyn = GetDynamicObject(new Dictionary<string, object>()
    {
        {"prop1", 12},
    });

Console.WriteLine(dyn.prop1);
dyn.prop1 = 150;

Pochodzenie z DynamicObject pozwala ci wymyślić własną strategię obsługi tych dynamicznych żądań członków, uważaj, że są tutaj potwory: kompilator nie będzie w stanie zweryfikować wielu twoich dynamicznych wywołań i nie uzyskasz inteligencji, więc po prostu zachowaj mając to na uwadze.

Clint
źródło
39

Dzięki @Clint za świetną odpowiedź:

Chciałem tylko podkreślić, jak łatwo było to rozwiązać za pomocą obiektu Expando:

    var dynamicObject = new ExpandoObject() as IDictionary<string, Object>;
    foreach (var property in properties) {
        dynamicObject.Add(property.Key,property.Value);
    }
Paul Grimshaw
źródło
3

możesz deserializować swój ciąg json do słownika, a następnie dodać nowe właściwości, a następnie serializować go.

 var jsonString = @"{}";

        var jsonDoc = JsonSerializer.Deserialize<Dictionary<string, object>>(jsonString);

        jsonDoc.Add("Name", "Khurshid Ali");

        Console.WriteLine(JsonSerializer.Serialize(jsonDoc));
Khurshid Ali
źródło