كيفية كتابة فئة ديناميكوبجيكت مخصصة التي تدعم التهيئة الكائن
-
29-10-2019 - |
سؤال
في وثائق ل ديناميكوبجيكت, ، هناك مثال على أ DynamicDictionary
يتيح لك ذلك العمل مع قاموس كما لو كان فصلا ذا خصائص.
هنا هو الطبقة (تعديل طفيف للإيجاز):
public class DynamicDictionary : DynamicObject
{
Dictionary<string, object> _dictionary = new Dictionary<string, object>();
public int Count
{
get { return _dictionary.Count; }
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
string name = binder.Name.ToLower();
return _dictionary.TryGetValue(name, out result);
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
_dictionary[binder.Name.ToLower()] = value;
return true;
}
}
ما أود القيام به هو تعديل الطبقة ، حتى أستطيع أن أفعل ما يلي:
public class Test
{
public Test()
{
var result = Enumerable.Range(1, 5).Select(i => new DynamicDictionary
{
Id = i,
Foo = "Foo",
Bar = 2
});
}
}
أسئلة
- هل هذا ممكن?
- اذا نعم, كيف?
المحلول 3
اتضح ، وكنت قادرا على حل مشكلتي باستخدام لينق المدمج في ToDictionary()
الطريقة.
مثال:
public Test()
{
var data = Enumerable.Range(1, 5).Select(i => new
{
Id = i,
Foo = "Foo",
Bar = 2
});
var result = data
.Select(d => d.GetType().GetProperties()
.Select(p => new { Name = p.Name, Value = p.GetValue(pd, null) })
.ToDictionary(
pair => pair.Name,
pair => pair.Value == null ? string.Empty : pair.Value.ToString()));
}
نصائح أخرى
DynamicObject
يوفر TryCreateInstance()
, ، وهو مخصص لحالات مثل هذه ، لكنه غير قابل للاستخدام من ج#.
أرى بعض الطرق حول هذا:
إنشاء فئة مصنع ديناميكية.عند استدعاء لها
Create()
الطريقة مع الجدال المسمى ، فإنه يمررها إلى القاموس:class DynamicDictionaryFactory : DynamicObject { public override bool TryInvokeMember( InvokeMemberBinder binder, object[] args, out object result) { if (binder.Name == "Create") { // use binder.CallInfo.ArgumentNames and args // to create the dynamic dictionary result = …; return true; } return base.TryInvokeMember(binder, args, out result); } } … dynamic factory = new DynamicDictionaryFactory(); dynamic dict = factory.Create(Id: 42);
استخدم مهيئ المجموعة غير الديناميكي.وهذا يعني وجود أسماء الخصائص كسلاسل في التعليمات البرمجية:
// has to implement IEnumerable, so that collection initializer works class DynamicDictionary : DynamicObject, IEnumerable<KeyValuePair<string, object>> { public void Add(string name, object value) { m_dictionary.Add(name, value); } // IEnumerable implmentation and actual DynamicDictionary code here } … dynamic dict = new DynamicDictionary { { "Id", 42 } };
ربما يكون الأقرب إلى ما طلبته هو استخدام متداخلة كائن مهيئ.وهذا هو ، فإن الطبقة لديها
dynamic
الملكية (قل,Values
) ، التي يمكن تعيين خصائصها باستخدام مهيئ الكائن:class DynamicDictionary : DynamicObject { private readonly IDictionary<string, object> m_expandoObject = new ExpandoObject(); public dynamic Values { get { return m_expandoObject; } } // DynamicDictionary implementation that uses m_expandoObject here } … dynamic dict = new DynamicDictionary { Values = { Id = 42 } };
باستخدام ImpromptuInterface مفتوح المصدر (عبر nuget) يحتوي على Builder Syntax .يتيح لك إجراء شيء قريب من بناء جملة التهيئة.على وجه التحديد بعد تضمين الرمز العام ، يمكنك القيام بذلك Genacodicetagpre
هناك خيارات أخرى مدرجة أيضًا في صفحة بناء الجملة هذه ، إذا قمت بإسقاط رمز الترقيم العام ، فسيستخدم رمز ترميز عام وهو نفس الشيء في الأساس.ويمكنك إلقاء نظرة على المصدر لبناء جملة أيضا.
When I see a pattern of embedded language like this, I tend to use extension methods, so I could write the following code to achieve my goal:
public Test()
{
var data = Enumerable.Range(1, 5).Select(i => new
{
Id = i,
Foo = "Foo",
Bar = 2
}.AsDynamicDictionary());
}
Then I would define the extension method with the code to convert any object
(including anonymously typed ones) to DynamicDictionary
:
public static class DynamicDictionaryExtensions
{
public static DynamicDictionary AsDynamicDictionary(this object data)
{
if (data == null) throw new ArgumentNullException("data");
return new DynamicDictionary(
data.GetType().GetProperties()
.Where(p => p. && p.CanRead)
.Select(p => new {Name: p.Name, Value: p.GetValue(data, null)})
.ToDictionary(p => p.Name, p => p.Value)
);
}
}
You would have to implement a constructor in DynamicDictionary
to receive a IDictionary<string, object>
, but that's a piece of cake.