using System; using System.Windows.Forms; using System.Collections.Generic; using System.ComponentModel; using System.Runtime.CompilerServices; static class Program { [STAThread] static void Main() { Customer Customer = new Customer(); Customer.AddProperty<string>("Name"); Customer.SetValue("Name", "Alex"); MessageBox.Show(Customer.GetStringValue("Name")); } } // Runtime extensible business object public class Customer : EntityBase<Customer> { string id; public string Id { get { return id; } set { id = value; } } } // from core library public abstract class EntityBase<T> : Bag { public virtual void SetValue(string propertyName, string propertyValue) { PropertyDescriptor p = TypeDescriptor.GetProperties(typeof(T))[propertyName]; ITypeDescriptorContext ctx = new SimpleContext(this, p); object val; val = p.Converter.ConvertFromString(ctx, propertyValue); p.SetValue(this, val); } public virtual string GetStringValue(string propertyName) { PropertyDescriptor p = TypeDescriptor.GetProperties(typeof(T))[propertyName]; return p.GetValue(this).ToString(); } } interface IBag { void OnAfterValueChanged(string propertyName); void AddHandler(object key, EventHandler value); void RemoveHandler(object key, EventHandler value); void OnEvent(object key); IBagValue GetValue(string propertyName); } interface IBagValue { void ResetValue(); bool IsDefaultValue { get;} object Value { get; set;} event EventHandler ValueChanged; } interface IBagDefinition { IBagValue Create(IBag bag); PropertyDescriptor Property { get;} } sealed class BagDefinition<T> : IBagDefinition { private readonly PropertyDescriptor property; public PropertyDescriptor Property { get { return property; } } private readonly T defaultValue; public T DefaultValue { get { return defaultValue; } } public string Name { get { return Property.Name; } } IBagValue IBagDefinition.Create(IBag bag) { return new BagValue<T>(bag, this); } public BagDefinition(string propertyName, Attribute[] attributes) { defaultValue = default(T); if (attributes != null) { // check for a default value foreach (Attribute attrib in attributes) { DefaultValueAttribute defAttrib = attrib as DefaultValueAttribute; if (defAttrib != null) { defaultValue = (T)defAttrib.Value; break; } } } property = new BagPropertyDescriptor(propertyName, attributes); } internal class BagPropertyDescriptor : PropertyDescriptor { public BagPropertyDescriptor(string name, Attribute[] attributes) : base(name, attributes) { } private IBagValue GetBagValue(object component) { return ((IBag)component).GetValue(Name); } public override object GetValue(object component) { return GetBagValue(component).Value; } public override void SetValue(object component, object value) { GetBagValue(component).Value = value; } public override Type ComponentType { get { return typeof(Bag); } } public override Type PropertyType { get { return typeof(T); } } public override bool IsReadOnly { get { return false; } } public override bool CanResetValue(object component) { return true; } public override void ResetValue(object component) { GetBagValue(component).ResetValue(); } public override bool ShouldSerializeValue(object component) { return !GetBagValue(component).IsDefaultValue; } public override bool SupportsChangeEvents { get { return true; } } public override void AddValueChanged(object component, EventHandler handler) { GetBagValue(component).ValueChanged += handler; } public override void RemoveValueChanged(object component, EventHandler handler) { GetBagValue(component).ValueChanged -= handler; } } } sealed class BagValue<T> : IBagValue, ITypeDescriptorContext { private T value; private readonly IBag bag; void IBagValue.ResetValue() { Value = Definition.DefaultValue; } bool IBagValue.IsDefaultValue { get { return EqualityComparer<T>.Default.Equals(Value, Definition.DefaultValue); } } private readonly BagDefinition<T> definition; public IBag Bag { get { return bag; } } public BagDefinition<T> Definition { get { return definition; } } public BagValue(IBag bag, BagDefinition<T> definition) { if (bag == null) throw new ArgumentNullException("bag"); if (definition == null) throw new ArgumentNullException("definition"); this.bag = bag; this.definition = definition; Value = Definition.DefaultValue; } public T Value { get { return value; } set { if (EqualityComparer<T>.Default.Equals(Value, value)) return; this.value = value; Bag.OnAfterValueChanged(Definition.Name); Bag.OnEvent(Definition); } } public event EventHandler ValueChanged { add { Bag.AddHandler(Definition, value); } remove { Bag.RemoveHandler(Definition, value); } } object IBagValue.Value { get { return Value; } set { Value = (T)value; } } IContainer ITypeDescriptorContext.Container { get { return null; } } object ITypeDescriptorContext.Instance { get { return Bag; } } void ITypeDescriptorContext.OnComponentChanged() { Bag.OnAfterValueChanged(Definition.Name); } bool ITypeDescriptorContext.OnComponentChanging() { return true; } PropertyDescriptor ITypeDescriptorContext.PropertyDescriptor { get { return Definition.Property; } } object IServiceProvider.GetService(Type serviceType) { return null; } } public /*sealed*/ class Bag : IBag, INotifyPropertyChanged { private EventHandlerList events; void IBag.AddHandler(object key, EventHandler handler) { AddHandler(key, handler); } void IBag.RemoveHandler(object key, EventHandler handler) { RemoveHandler(key, handler); } void IBag.OnEvent(object key) { OnEvent(key); } private void AddHandler(object key, Delegate handler) { if (handler == null) return; if (events == null) events = new EventHandlerList(); events.AddHandler(key, handler); } private void RemoveHandler(object key, Delegate handler) { if (events == null || handler == null) return; events.RemoveHandler(key, handler); } private void OnEvent(object key) { if (events == null) return; EventHandler handler = events[key] as EventHandler; if (handler != null) handler(this, EventArgs.Empty); } public event PropertyChangedEventHandler PropertyChanged { add { AddHandler(EVENT_PropertyChanged, value); } remove { RemoveHandler(EVENT_PropertyChanged, value); } } private static readonly object EVENT_PropertyChanged = new object(); private void OnPropertyChanged(string propertyName) { if (events == null) return; PropertyChangedEventHandler handler = events[EVENT_PropertyChanged] as PropertyChangedEventHandler; if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); } void IBag.OnAfterValueChanged(string propertyName) { OnPropertyChanged(propertyName); } IBagValue IBag.GetValue(string propertyName) { return GetValue(propertyName); } private readonly Dictionary<string, IBagValue> values = new Dictionary<string, IBagValue>(StringComparer.InvariantCulture); private IBagValue GetValue(string propertyName) { lock (values) { IBagValue value; if (!values.TryGetValue(propertyName, out value)) { value = CreateValue(this, propertyName); values.Add(propertyName, value); } return value; } } static readonly Dictionary<string, IBagDefinition> defintions = new Dictionary<string, IBagDefinition>(StringComparer.InvariantCulture); public static PropertyDescriptor AddProperty<T>(string propertyName, params Attribute[] attributes) { BagDefinition<T> def = new BagDefinition<T>(propertyName, attributes); lock (defintions) { defintions.Add(propertyName, def); BagDescriptionProvider.ResetProperties(); } return def.Property; } internal static PropertyDescriptorCollection GetProperties() { lock (defintions) { PropertyDescriptor[] props = new PropertyDescriptor[defintions.Count]; int i = 0; foreach (IBagDefinition def in defintions.Values) { props[i++] = def.Property; } return new PhantomPropertyDescriptorCollection(props, false); } } internal sealed class PhantomPropertyDescriptorCollection : PropertyDescriptorCollection { public PhantomPropertyDescriptorCollection(PropertyDescriptor[] properties, bool readOnly) : base(properties, readOnly) { } public override PropertyDescriptor Find(string name, bool ignoreCase) { PropertyDescriptor prop = base.Find(name, ignoreCase); if (prop == null) { prop = AddProperty<object>(name); Add(prop); } return prop; } } static IBagValue CreateValue(IBag bag, string propertyName) { lock (defintions) { return defintions[propertyName].Create(bag); } } public Bag() { } static Bag() { BagDescriptionProvider.Initialize(); } } sealed class BagDescriptionProvider : TypeDescriptionProvider { static readonly BagTypeDescriptor descriptor; [MethodImpl(MethodImplOptions.NoInlining)] internal static void Initialize() { } // to force static ctor static BagDescriptionProvider() { ICustomTypeDescriptor parent = TypeDescriptor.GetProvider(typeof(Bag)).GetTypeDescriptor(typeof(Bag)); descriptor = new BagTypeDescriptor(parent); TypeDescriptor.AddProvider(new BagDescriptionProvider(), typeof(Bag)); } public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance) { return descriptor; } internal static void ResetProperties() { descriptor.ResetProperties(); } } sealed class BagTypeDescriptor : CustomTypeDescriptor { public BagTypeDescriptor(ICustomTypeDescriptor parent) : base(parent) { if (parent == null) throw new ArgumentNullException("parent"); } private PropertyDescriptorCollection properties; internal void ResetProperties() { properties = null; } public override PropertyDescriptorCollection GetProperties(Attribute[] attributes) { return GetProperties(); } public override PropertyDescriptorCollection GetProperties() { if (properties == null) { properties = Bag.GetProperties(); } return properties; } } [ImmutableObject(true)] class SimpleContext : ITypeDescriptorContext { readonly object instance; readonly PropertyDescriptor property; internal SimpleContext(object instance, PropertyDescriptor property) { this.instance = instance; this.property = property; } public IContainer Container { get { return null; } } public object Instance { get { return instance; } } public void OnComponentChanged() { } public bool OnComponentChanging() { return true; } public PropertyDescriptor PropertyDescriptor { get { return property; } } public object GetService(Type serviceType) { return null; } }>Thank you Andrus. It's going to take me a while to try your code, but it will be very educational.