إنشاء مثيل من النوع دون منشئ افتراضي في C # باستخدام انعكاس
-
23-08-2019 - |
سؤال
خذ الطبقة التالية كمثال:
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
)
ينشئ مثيل من النوع المحدد باستخدام المنشئ الذي يطابق أفضل المعلمات المحددة.
عندما معيار أداء (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);
}
}