سؤال

هل هناك طريقة في C# حيث يمكنني استخدام انعكاس لوضع كائن الملكية ؟

مثلا:

MyObject obj = new MyObject();
obj.Name = "Value";

أريد أن مجموعة obj.Name مع التأمل.شيء من هذا القبيل:

Reflection.SetProperty(obj, "Name") = "Value";

هل هناك طريقة للقيام بذلك ؟

هل كانت مفيدة؟

المحلول

نعم، يمكنك استخدام Type.InvokeMember():

using System.Reflection;
MyObject obj = new MyObject();
obj.GetType().InvokeMember("Name",
    BindingFlags.Instance | BindingFlags.Public | BindingFlags.SetProperty,
    Type.DefaultBinder, obj, "Value");

وهذا سوف بطرح استثناء إذا لم يكن obj خاصية تسمى Name، أو أنه لا يمكن تعيين.

وثمة نهج آخر هو الحصول على البيانات الوصفية للخاصية، ومن ثم وضعها. هذا سيسمح لك للتحقق من وجود العقار، والتحقق من أنه يمكن تعيين:

using System.Reflection;
MyObject obj = new MyObject();
PropertyInfo prop = obj.GetType().GetProperty("Name", BindingFlags.Public | BindingFlags.Instance);
if(null != prop && prop.CanWrite)
{
    prop.SetValue(obj, "Value", null);
}

نصائح أخرى

ويمكنك أيضا القيام:

Type type = target.GetType();

PropertyInfo prop = type.GetProperty("propertyName");

prop.SetValue (target, propertyValue, null);

وحيث الهدف هو الكائن الذي سيكون له مجموعة ممتلكاتها.

والتأمل، في الأساس، أي بمعنى.

myObject.GetType().GetProperty(property).SetValue(myObject, "Bob", null);

وأو هناك مكتبات للمساعدة سواء من حيث الراحة والأداء؛ على سبيل المثال مع FastMember :

var wrapped = ObjectAccessor.Create(obj); 
wrapped[property] = "Bob";

و(التي لديها أيضا ميزة عدم الحاجة إلى معرفة مقدما ما إذا كان الحقل مقابل خاصية)

وأو هل يمكن أن التفاف مارك لبطانة واحد داخل الطبقة تمديد الخاصة بك:

public static class PropertyExtension{       

   public static void SetPropertyValue(this object obj, string propName, object value)
    {
        obj.GetType().GetProperty(propName).SetValue(obj, value, null);
    }
}

ووالذي يطلق عليه مثل هذا:

myObject.SetPropertyValue("myProperty", "myValue");

لحسن التدبير، دعونا نضيف طريقة للحصول على قيمة العقار:

public static object GetPropertyValue(this object obj, string propName)
{
        return obj.GetType().GetProperty(propName).GetValue (obj, null);
}

نعم، وذلك باستخدام System.Reflection:

using System.Reflection;

...

    string prop = "name";
    PropertyInfo pi = myObject.GetType().GetProperty(prop);
    pi.SetValue(myObject, "Bob", null);

ويمكنك أيضا الوصول إلى حقول باستخدام بطريقة مشابهة:

var obj=new MyObject();
FieldInfo fi = obj.GetType().
  GetField("Name", BindingFlags.NonPublic | BindingFlags.Instance);
fi.SetValue(obj,value)

ومع انعكاس كل شيء يمكن أن يكون كتاب مفتوح :) في بلدي على سبيل المثال نحن ملزمة إلى حقل مستوى المثال الخاص.

استخدم سمثينغس مثل هذا:

public static class PropertyExtension{       

   public static void SetPropertyValue(this object p_object, string p_propertyName, object value)
   {
    PropertyInfo property = p_object.GetType().GetProperty(p_propertyName);
    property.SetValue(p_object, Convert.ChangeType(value, property.PropertyType), null);
   }
}

أو

public static class PropertyExtension{       

   public static void SetPropertyValue(this object p_object, string p_propertyName, object value)
   {
    PropertyInfo property = p_object.GetType().GetProperty(p_propertyName);
    Type t = Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType;
    object safeValue = (value == null) ? null : Convert.ChangeType(value, t);

    property.SetValue(p_object, safeValue, null);
   }
}

ويمكنك محاولة ذلك عندما تريد خصائص الكتلة تعيين لكائن من كائن آخر باستخدام أسماء العقار:

public static void Assign(this object destination, object source)
    {
        if (destination is IEnumerable && source is IEnumerable)
        {
            var dest_enumerator = (destination as IEnumerable).GetEnumerator();
            var src_enumerator = (source as IEnumerable).GetEnumerator();
            while (dest_enumerator.MoveNext() && src_enumerator.MoveNext())
                dest_enumerator.Current.Assign(src_enumerator.Current);
        }
        else
        {
            var destProperties = destination.GetType().GetProperties();
            foreach (var sourceProperty in source.GetType().GetProperties())
            {
                foreach (var destProperty in destProperties)
                {
                    if (destProperty.Name == sourceProperty.Name && destProperty.PropertyType.IsAssignableFrom(sourceProperty.PropertyType))
                    {
                        destProperty.SetValue(destination,     sourceProperty.GetValue(source, new object[] { }), new object[] { });
                        break;
            }
        }
    }
}

ولقد نشرت للتو مجموعة Nuget التي تسمح بإنشاء ليس فقط خصائص المستوى الأول ولكن خصائص متداخلة أيضا في كائن معين في أي عمق.

وهنا حزمة

وتعيين قيمة خاصية كائن طريقها من جذورها.

والكائن يمكن أن يكون كائن معقد والخاصية يمكن أن يكون مستوى متعدد الملكية متداخلة عميقة أو أنها يمكن أن تكون الملكية مباشرة تحت الجذر. سوف ObjectWriter العثور على الممتلكات باستخدام المعلمة مسار الممتلكات وتحديث قيمته. مسار الملكية أسماء إلحاق من الخصائص التي تمت زيارتها من الجذر إلى الملكية عقدة نهاية التي نريد لتعيين، محدد من قبل المعلمة سلسلة محدد.

والاستعمال:

لإنشاء خصائص مباشرة تحت الجذر الكائن:

وبمعنى آخر. الطبقة LineItem ديها خاصية الباحث دعا ItemId

LineItem lineItem = new LineItem();

ObjectWriter.Set(lineItem, "ItemId", 13, delimiter: null);

لإنشاء الملكية مستويات متعددة متداخلة تحت الجذر الكائن:

وبمعنى آخر. الطبقة Invite لديها خاصية تسمى State، التي لديها خاصية تسمى Invite (من دعوة نوع)، الذي يحتوي على خاصية تسمى Recipient، التي لديها خاصية تسمى Id.

لجعل الأمور أكثر تعقيدا، الخاصية State ليس نوع مرجع، بل هو struct.

وهنا هو كيف يمكنك تعيين الخاصية رقم (إلى قيمة سلسلة من "آفاق") في الجزء السفلي من الشجرة كائن في سطر واحد.

Invite invite = new Invite();

ObjectWriter.Set(invite, "State_Invite_Recipient_Id", "outlook", delimiter: "_");

على أساس MarcGravell اقتراح, وقد شيدت التالية أسلوب ثابت.طريقة بشكل عام يعين كل مطابقة خصائص الكائن المصدر إلى الهدف باستخدام FastMember

 public static void DynamicPropertySet(object source, object target)
    {
        //SOURCE
        var src_accessor = TypeAccessor.Create(source.GetType());
        if (src_accessor == null)
        {
            throw new ApplicationException("Could not create accessor!");
        }
        var src_members = src_accessor.GetMembers();
        if (src_members == null)
        {
            throw new ApplicationException("Could not fetch members!");
        }
        var src_class_members = src_members.Where(x => x.Type.IsClass && !x.Type.IsPrimitive);
        var src_class_propNames = src_class_members.Select(x => x.Name);
        var src_propNames = src_members.Except(src_class_members).Select(x => x.Name);

        //TARGET
        var trg_accessor = TypeAccessor.Create(target.GetType());
        if (trg_accessor == null)
        {
            throw new ApplicationException("Could not create accessor!");
        }
        var trg_members = trg_accessor.GetMembers();
        if (trg_members == null)
        {
            throw new ApplicationException("Could not create accessor!");
        }
        var trg_class_members = trg_members.Where(x => x.Type.IsClass && !x.Type.IsPrimitive);
        var trg_class_propNames = trg_class_members.Select(x => x.Name);
        var trg_propNames = trg_members.Except(trg_class_members).Select(x => x.Name);



        var class_propNames = trg_class_propNames.Intersect(src_class_propNames);
        var propNames = trg_propNames.Intersect(src_propNames);

        foreach (var propName in propNames)
        {
            trg_accessor[target, propName] = src_accessor[source, propName];
        }
        foreach (var member in class_propNames)
        {
            var src = src_accessor[source, member];
            var trg = trg_accessor[target, member];
            if (src != null && trg != null)
            {
                DynamicPropertySet(src, trg);
            }
        }
    }
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top