Expandoobject сделать объекты расширяемыми
// Use the DynamicBase<T> class derived from DynamicObject to create a new class or
// encapsulate an existing class:
public class DynamicBase<T> : DynamicObject
where T : new()
{
private T _containedObject = default(T);
[JsonExtensionData] //JSON.NET 5.0 and above
private Dictionary<string, object> _dynamicMembers =
new Dictionary<string, object>();
private List<PropertyInfo> _propertyInfos =
new List<PropertyInfo>(typeof(T).GetProperties());
public DynamicBase()
{
}
public DynamicBase(T containedObject)
{
_containedObject = containedObject;
}
public override bool TryInvokeMember(InvokeMemberBinder binder,
object[] args, out object result)
{
if (_dynamicMembers.ContainsKey(binder.Name)
&& _dynamicMembers[binder.Name] is Delegate)
{
result = (_dynamicMembers[binder.Name] as Delegate).DynamicInvoke(
args);
return true;
}
return base.TryInvokeMember(binder, args, out result);
}
public override IEnumerable<string> GetDynamicMemberNames() =>
_dynamicMembers.Keys;
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = null;
var propertyInfo = _propertyInfos.Where(pi =>
pi.Name == binder.Name).FirstOrDefault();
// Make sure this member isn't a property on the object yet
if (propertyInfo == null)
{
// look in the additional items collection for it
if (_dynamicMembers.Keys.Contains(binder.Name))
{
// return the dynamic item
result = _dynamicMembers[binder.Name];
return true;
}
}
else
{
// get it from the contained object
if (_containedObject != null)
{
result = propertyInfo.GetValue(_containedObject);
return true;
}
}
return base.TryGetMember(binder, out result);
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
var propertyInfo = _propertyInfos.Where(pi =>
pi.Name == binder.Name).FirstOrDefault();
// Make sure this member isn't a property on the object yet
if (propertyInfo == null)
{
// look in the additional items collection for it
if (_dynamicMembers.Keys.Contains(binder.Name))
{
// set the dynamic item
_dynamicMembers[binder.Name] = value;
return true;
}
else
{
_dynamicMembers.Add(binder.Name, value);
return true;
}
}
else
{
// put it in the contained object
if (_containedObject != null)
{
propertyInfo.SetValue(_containedObject, value);
return true;
}
}
return base.TrySetMember(binder, value);
}
public override string ToString()
{
StringBuilder builder = new StringBuilder();
foreach (var propInfo in _propertyInfos)
{
if(_containedObject != null)
builder.AppendFormat("{0}:{1}{2}", propInfo.Name,
propInfo.GetValue(_containedObject), Environment.NewLine);
else
builder.AppendFormat("{0}:{1}{2}", propInfo.Name,
propInfo.GetValue(this), Environment.NewLine);
}
foreach (var addlItem in _dynamicMembers)
{
// exclude methods that are added from the description
Type itemType = addlItem.Value.GetType();
Type genericType =
itemType.IsGenericType ?
itemType.GetGenericTypeDefinition() : null;
if (genericType != null)
{
if (genericType != typeof(Func<>) &&
genericType != typeof(Action<>))
builder.AppendFormat("{0}:{1}{2}", addlItem.Key,
addlItem.Value, Environment.NewLine);
}
else
builder.AppendFormat("{0}:{1}{2}", addlItem.Key, addlItem.Value,
Environment.NewLine);
}
return builder.ToString();
}
}
// To understand how DynamicBase<T> is used, consider a scenario where we have a web
// service that is receiving a serialized JSON payload of athlete information. Currently we
// have defined the DynamicAthlete class with properties for both a Name and a Sport:
public class DynamicAthlete : DynamicBase<DynamicAthlete>
{
public string Name { get; set; }
public string Sport { get; set; }
}
// Create a set of information on athletes
// Note that the service receiving these doesn't have Position as a
// property on the Athlete object
dynamic initialAthletes = new[]
{
new
{
Name = "Tom Brady",
Sport = "Football",
Position = "Quarterback"
},
new
{
Name = "Derek Jeter",
Sport = "Baseball",
Position = "Shortstop"
},
new
{
Name = "Michael Jordan",
Sport = "Basketball",
Position = "Small Forward"
},
new
{
Name = "Lionel Messi",
Sport = "Soccer",
Position = "Forward"
}
};
// serialize the JSON to send to a web service about athletes…
string serializedAthletes = JsonNetSerialize(initialAthletes);
// Assume the JSON payload for the athletes comes in to your service and is deserialized
// (once again, props to JSON.NET) and we deserialize it as an array of DynamicAthletes:
// deserialize the JSON we were sent
var athletes = JsonNetDeserialize<DynamicAthlete[]>(serializedAthletes);
Someone who wants to know