إنشاء مثيل من النوع دون منشئ افتراضي في C # باستخدام انعكاس

StackOverflow https://stackoverflow.com/questions/390578

سؤال

خذ الطبقة التالية كمثال:

class Sometype
{
    int someValue;

    public Sometype(int someValue)
    {
        this.someValue = someValue;
    }
}

ثم أريد إنشاء مثيل من هذا النوع باستخدام انعكاس:

Type t = typeof(Sometype);
object o = Activator.CreateInstance(t);

عادة هذا سيعمل، ولكن SomeType لم يعرف منشئا بلا حدود، الدعوة إلى Activator.CreateInstance سوف رمي استثناء من النوع MissingMethodException مع الرسالة "لا يوجد منشئ بلا حدود محددة لهذا الكائن."هل هناك طريقة بديلة لا تزال تؤدي إلى إنشاء مثيل من هذا النوع؟ يبدو أن كيندا سوكي لإضافة منشئين بلا حدود إلى جميع فصولي.

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

المحلول

أنا في الأصل نشرت هذه الإجابة هنا, ، ولكن هنا هو إعادة طبع لأن هذا ليس هو نفس السؤال بالضبط ولكن لديه نفس الجواب:

FormatterServices.GetUninitializedObject() سيخلق مثيل دون استدعاء منشئ. لقد وجدت هذه الفئة باستخدام العاكس والحفر من خلال بعض فصول التسلسل الأساسية .NET.

اختبرت ذلك باستخدام نموذج التعليمات البرمجية أدناه ويبدو أنه يعمل بشكل رائع:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Runtime.Serialization;

namespace NoConstructorThingy
{
    class Program
    {
        static void Main(string[] args)
        {
            MyClass myClass = (MyClass)FormatterServices.GetUninitializedObject(typeof(MyClass)); //does not call ctor
            myClass.One = 1;
            Console.WriteLine(myClass.One); //write "1"
            Console.ReadKey();
        }
    }

    public class MyClass
    {
        public MyClass()
        {
            Console.WriteLine("MyClass ctor called.");
        }

        public int One
        {
            get;
            set;
        }
    }
}

نصائح أخرى

استخدم هذا الحمل الزائد طريقة CreateInstance:

public static Object CreateInstance(
    Type type,
    params Object[] args
)

ينشئ مثيل من النوع المحدد باستخدام المنشئ الذي يطابق أفضل المعلمات المحددة.

يرى: http://msdn.microsoft.com/en-us/library/wcxyzt4d.aspx.

عندما معيار أداء (T)FormatterServices.GetUninitializedObject(typeof(T)) كان أبطأ. في الوقت نفسه، ستمنحك تعبيرات مجمعة تحسينات كبيرة على الرغم من أنها تعمل فقط لأنواع المنشئ الافتراضي. أخذت نهجا مختلفا:

public static class New<T>
{
    public static readonly Func<T> Instance = Creator();

    static Func<T> Creator()
    {
        Type t = typeof(T);
        if (t == typeof(string))
            return Expression.Lambda<Func<T>>(Expression.Constant(string.Empty)).Compile();

        if (t.HasDefaultConstructor())
            return Expression.Lambda<Func<T>>(Expression.New(t)).Compile();

        return () => (T)FormatterServices.GetUninitializedObject(t);
    }
}

public static bool HasDefaultConstructor(this Type t)
{
    return t.IsValueType || t.GetConstructor(Type.EmptyTypes) != null;
}

هذا يعني أن إنشاء تعبير مؤقت يتم تخزينها بشكل فعال وتتحمل عقوبة فقط في المرة الأولى التي يتم فيها تحميل النوع. سوف تعامل مع أنواع القيمة أيضا بطريقة فعالة.

نسميها:

MyType me = New<MyType>.Instance();

لاحظ أن (T)FormatterServices.GetUninitializedObject(t) سوف تفشل في السلسلة. وبالتالي، فإن المناولة الخاصة بالسلسلة في مكان لإعادة سلسلة فارغة.

إجابات جيدة ولكن غير صالحة للاستعمال على إطار Dot Net Compact. هنا هو الحل الذي سيعمل على CF.NET ...

class Test
{
    int _myInt;

    public Test(int myInt)
    {
        _myInt = myInt;
    }

    public override string ToString()
    {
        return "My int = " + _myInt.ToString();
    }
}

class Program
{
    static void Main(string[] args)
    {
        var ctor = typeof(Test).GetConstructor(new Type[] { typeof(int) });
        var obj = ctor.Invoke(new object[] { 10 });
        Console.WriteLine(obj);
    }
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top