Получить * значение * пользовательского свойства для экземпляра объекта? - PullRequest
3 голосов
/ 25 октября 2011

Использование .NET 4, C #

Допустим, у меня есть класс Info, который расширяет CustomTypeDescriptor. Экземпляр класса Info имеет словарь из <string, object> пар, загруженных во время выполнения.

Я бы хотел иметь возможность выставлять ключи словаря как свойства (чтобы каждый экземпляр Info имел разные свойства). Значения свойств должны быть соответствующими значениями в словаре.

Я дошел до разоблачения свойств:

    public override PropertyDescriptorCollection GetProperties()
    {

        var orig = base.GetProperties();
        var newProps = dictionary.Select( kvp => 
                       TypeDescriptor.CreateProperty(
                           this.GetType(), 
                           kvp.key, 
                           typeof(string)));
        return new PropertyDescriptorCollection(
                   orig.Cast<PropertyDescriptor>()
                   .Concat(newProps)
                   .ToArray());
    }

Проблема в том, как мне получить их значения?

var info = new Info(new Dictionary<string, object>{{"some_property", 5}};
var prop = TypeDescriptor.GetProperties(i_info)["some_property"];
var val = prop.GetValue(i_info); //should return 5

Единственный способ получить управление при вызове prop.GetValue() - это переопределить GetPropertyOwner(PropertyDescriptor pd), но, как я понимаю, он ожидает, что я верну экземпляр другого типа , который имеет сопоставление реального (скомпилированного) свойства.

Я бы хотел сам написать фактическую реализацию свойства (для этого примера вернуть значение в словаре, ключ которого соответствует имени свойства).

Возможно ли это?

Ответы [ 2 ]

3 голосов
/ 25 октября 2011

Вам необходимо создать собственную реализацию метода PropertyDescriptor, переопределяющего класс GetValue.Таким образом, вместо TypeDescriptor.CreateProperty вы будете использовать новый MyCoolPropertyDescriptor(dictionary, kvp.Key) или подобный.

Вот пример того, как это может быть реализовано:


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;

namespace ConsoleApplication1
{
    internal sealed class MyCoolPropertyDescriptor : PropertyDescriptor
    {
        private Func<object, object> propertyGetter;
        private Action<object, object> propertySetter;

        public MyCoolPropertyDescriptor(
            string name,
            Func<object, object> propertyGetter,
            Action<object, object> propertySetter)
            : base(name, new Attribute[] {})
        {
            this.propertyGetter = propertyGetter;
            this.propertySetter = propertySetter;
        }

        public override bool CanResetValue(object component)
        {
            return true;
        }

        public override System.Type ComponentType
        {
            get { return typeof(object); }
        }

        public override object GetValue(object component)
        {
            return this.propertyGetter(component);
        }

        public override bool IsReadOnly
        {
            get { return false; }
        }

        public override System.Type PropertyType
        {
            get { return typeof(object); }
        }

        public override void ResetValue(object component)
        {
            this.propertySetter(component, null);
        }

        public override void SetValue(object component, object value)
        {
            this.propertySetter(component, value);
        }

        public override bool ShouldSerializeValue(object component)
        {
            return false;
        }
    }

    public sealed class Info : CustomTypeDescriptor
    {
        IDictionary<string, object> properties;

        public Info(IDictionary<string, object> properties)
        {
            this.properties = properties;
        }

        public override PropertyDescriptorCollection GetProperties()
        {
            var orig = base.GetProperties();
            var newProps = this.properties
                .Select(kvp => new MyCoolPropertyDescriptor(
                    kvp.Key,
                    o => ((Info)o).properties[kvp.Key],
                    (o, v) => ((Info)o).properties[kvp.Key] = v));

            return new PropertyDescriptorCollection(orig
                .Cast<PropertyDescriptor>()
                .Concat(newProps)
                .ToArray());
        }
    }

    internal class Program
    {
        private static void Main(string[] args)
        {
            var info = new Info(new Dictionary<string, object>{{"some_property", 5}});
            var prop = TypeDescriptor.GetProperties(info)["some_property"];
            var val = prop.GetValue(info); //should return 5
            Console.WriteLine(val);
        }
    }
}

2 голосов
/ 25 октября 2011

Мое понимание CustomTypeDescriptor состоит в том, что он позволяет привязке данных предоставлять дополнительные свойства, скажем, сетке, которые на самом деле не существуют в классе.Это не то, что расширяет CLR, так что ваш фактический класс предоставляет свойство.

Если вы хотите реальные свойства CLR, вам нужно взглянуть на DynamicObject или ExpandoObject, чтобы получить ту функциональность, которая мне кажетсяВы после.

...