سؤال

بادئ ذي بدء ، آسف إذا تم طلب ذلك من قبل. لقد أجريت بحثًا شاملاً ولم أجد شيئًا مثله تمامًا ، لكنني ربما فاتني شيء ما.

والآن على السؤال: أحاول استدعاء مُنشئ من خلال التفكير ، دون حظ. في الأساس ، لدي كائن أريد استنساخه ، لذلك أبحث عن مُنشئ النسخ لنوعه ثم أريد استدعاءه. هذا ما لدي:

public Object clone(Object toClone) {
     MethodBase copyConstructor = type.GetConstructor(
         new Type[] { toClone.GetType() });
     return method.Invoke(toClone, new object[] { toClone }); //<-- doesn't work
}

أسمي الطريقة أعلاه مثل ذلك:

List<int> list = new List<int>(new int[] { 0, 1, 2 });
List<int> clone = (List<int>) clone(list);

الآن ، لاحظ أن طريقة الاستدعاء التي أستخدمها هي MethodBaseاستدعاء. ConstructorInfo يوفر طريقة استدعاء تعمل إذا تم الاحتجاج بها مثل هذا:

return ((ConstructorInfo) method).Invoke(new object[] { toClone });

ومع ذلك ، أريد استخدام MethodBaseطريقة ، لأنه في الواقع بدلاً من البحث عن مُنشئ النسخ في كل مرة سأقوم فيها بتخزينها في القاموس ، ويحتوي القاموس على كل من الأساليب والمقدمات ، لذا فهو أ Dictionary<MethodBase>, ، ليس Dictionary<ConstructorInfo>. يمكنني بالطبع أن ألقي في ConstructorInfo كما أفعل أعلاه ، لكنني أفضل تجنب الصب واستخدام MethodBase الطريقة مباشرة. لا يمكنني معرفة المعلمات الصحيحة.

أي مساعدة؟ ًشكراً جزيلا.


تعديل

بنيامين،
شكرا جزيلا لاقتراحاتكم. كنت أفعل ما تقترحه بالضبط في التحرير الثاني ، باستثناء (وهذا "كبير" باستثناء ") كان القاموس الخاص بي هو المكان

class ClonerMethod {

    public MethodBase method;
    public bool isConstructor;

    ...

    public Object invoke(Object toClone) {
        return isConstructor ?
            ((ConstructorInfo) method).Invoke(new object[] { toClone }) : //<-- I wanted to avoid this cast
            method.Invoke(toClone, null);
    }

}

ثم اتصلت ClonerMethodinvoke على ما وجدته في القاموس. لم أضف الكود الصفقات مع كل ذلك لأن الإجابة التي كنت أبحث عنها كانت مجرد كيفية الاتصال بالاستدعاء على أ ConstructorInfo استخدام MethodBaseInvoke الطريقة ، لذلك لم أكن أرغب في إضافة معلومات غير ضرورية والكثير من التعليمات البرمجية لكم يا رفاق لقراءتها. ومع ذلك ، أحب استخدامك Func<,> أفضل بكثير ، لذلك أنا أتحول إلى ذلك. أيضا جعل Clone الطريقة العامة هي إضافة لطيفة ، ولكن في حالتي ، لا يعرف المتصل نوع الكائن ، لذلك سأبقيه غير واضحة بدلاً من ذلك.

لم أكن أعرف عنه Func<,>, ، وإذا علمت عن مشغل Lambda الذي نسيته (لم أكن بحاجة حقًا إلى شيء مثل هذا من قبل) ، لذلك تعلمت الكثير من إجابتك. أحب دائمًا أن أتعلم أشياء جديدة ، وسيكون هذا مفيدًا جدًا في المستقبل ، لذا شكرًا جزيلاً! قون

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

المحلول

إذا كنت تعلم أن الكائن لديه مُنشئ من هذا القبيل ، هل فكرت في استخدام هذا الحمل الزائد Activator.CreateInstance في حين أن؟


تحديث: لذلك لديك بحث متتالي عن MethodInfo/Methodbase بالفعل وتخزينها -> لا تريد/لا يمكنك استخدامها Activator.

في هذه الحالة ، لا أرى طريقة لفعل ما تريد بدون طاقم. ولكن - ربما يمكنك تغيير الهندسة المعمارية لتخزين أ Dictionary<Type, Func<object, object>> وإضافة تلك Func<> حالات بدلا من ذلك. يجعل رمز الاتصال أجمل (أفترض) وسيسمح لك بالقيام بهذا المدلى بها مرة واحدة:

// Constructor
dictionary.Add(type,
  source => ((ConstructorInfo) method).Invoke(new object[] {source})
);

// Clone
dictionary.Add(type,
  source => method.Invoke(source, new object[]{})
);

في الواقع ، نظرًا لأنك تهتم فقط بالفرق بين المنشئ والطريقة العادية في الموقع الذي تمسك به ، فلن تحتاج إلى ممثلين على الإطلاق ، أليس كذلك؟

// Constructor 2
dictionary.Add(type,
  source => yourConstructorInfo.Invoke(new object[] {source})
);

ما لم أكن أفتقد شيئًا (ممكنًا تمامًا ، بالطبع) ، يمكن أن يحل هذا المشكلة عن طريق القيام بذلك مرة واحدة على الجانب المحدد من السياج ولن يحتاج المتصل إلى الثقل إذا كان هذا مُنشئًا أم لا؟


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

public class Cloner {
    private readonly IDictionary<Type, Func<object, object>> _cloneMap =
            new Dictionary<Type, Func<object, object>>();

    public T Clone<T>(T source) {
        Type sourceType = source.GetType();
        Func<object, object> cloneFunc;

        if (_cloneMap.TryGetValue(sourceType, out cloneFunc)) {
            return (T)cloneFunc(source);
        }

        if (TryGetCopyConstructorCloneFunc(sourceType, out cloneFunc)) {
            _cloneMap.Add(sourceType, cloneFunc);
            return (T)cloneFunc(source);
        }

        if (TryGetICloneableCloneFunc(sourceType, out cloneFunc)) {
            _cloneMap.Add(sourceType, cloneFunc);
            return (T)cloneFunc(source);
        }

        return default(T);
    }

    private bool TryGetCopyConstructorCloneFunc(Type type, 
                    out Func<object, object> cloneFunc) {
        var constructor = type.GetConstructor(new[] { type });
        if (constructor == null) {
            cloneFunc = source => null;
            return false;
        }
        cloneFunc = source => constructor.Invoke(new[] { source });
        return true;
    }

    private bool TryGetICloneableCloneFunc(Type type,
                    out Func<object, object> cloneFunc) {
        bool isICloneable = typeof(ICloneable).IsAssignableFrom(type);
        var cloneMethod = type.GetMethod("Clone", new Type[] { });
        if (!isICloneable || (cloneMethod == null)) {
            cloneFunc = source => null;
            return false;
        }
        cloneFunc = source => cloneMethod.Invoke(source, new object[] {});
        return true;
    }
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top