การสร้างอินสแตนซ์ประเภทโดยไม่มีตัวสร้างเริ่มต้นใน C # โดยใช้การสะท้อน


98

ยกตัวอย่างชั้นเรียนต่อไปนี้:

class Sometype
{
    int someValue;

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

จากนั้นฉันต้องการสร้างอินสแตนซ์ประเภทนี้โดยใช้การสะท้อน:

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

โดยปกติสิ่งนี้จะใช้งานได้อย่างไรก็ตามเนื่องจากSomeTypeไม่ได้กำหนดคอนสตรัคเตอร์แบบไม่มีพารามิเตอร์การเรียกใช้Activator.CreateInstanceจะทำให้เกิดข้อยกเว้นของประเภทที่MissingMethodExceptionมีข้อความ " ไม่มีตัวสร้างที่ไม่มีพารามิเตอร์กำหนดไว้สำหรับอ็อบเจ็กต์นี้ " มีวิธีอื่นในการสร้างอินสแตนซ์ของประเภทนี้หรือไม่ มันเป็นเรื่องที่แย่มากที่จะเพิ่มตัวสร้างแบบไม่มีพารามิเตอร์ให้กับทุกชั้นเรียนของฉัน


2
FormatterServices.GetUninitializedObjectไม่อนุญาตให้สร้างสตริงที่ไม่ได้เริ่มต้น คุณอาจได้รับข้อยกเว้น: System.ArgumentException: Uninitialized Strings cannot be created.โปรดระลึกไว้เสมอ
Bartosz Pierzchlewicz

ขอบคุณสำหรับการแจ้งให้ทราบล่วงหน้า แต่ฉันจัดการสตริงและประเภทพื้นฐานแยกกันอยู่แล้ว
Aistina

คำตอบ:


143

เดิมฉันโพสต์คำตอบนี้ที่นี่แต่นี่เป็นการพิมพ์ซ้ำเนื่องจากนี่ไม่ใช่คำถามเดียวกัน แต่มีคำตอบเดียวกัน:

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;
        }
    }
}

เยี่ยมมากดูเหมือนว่านั่นคือสิ่งที่ฉันต้องการ ฉันถือว่า uninitialized หมายความว่าหน่วยความจำทั้งหมดจะถูกตั้งค่าเป็นศูนย์? (คล้ายกับการสร้างอินสแตนซ์ของโครงสร้าง)
Aistina

ค่าเริ่มต้นสำหรับแต่ละประเภทจะเป็นค่าเริ่มต้น ดังนั้นออบเจ็กต์จะเป็นโมฆะ ints 0 ฯลฯ ฉันคิดว่าการเริ่มต้นระดับคลาสใด ๆ เกิดขึ้น แต่ไม่มีการเรียกใช้ตัวสร้าง
Jason Jackson

14
@JSBangs นั่นแย่มากคุณกำลังหาคำตอบที่ถูกต้องตามกฎหมาย ความคิดเห็นของคุณและคำตอบอื่นไม่ได้ตอบคำถามที่ถามจริง หากคุณรู้สึกว่ามีคำตอบที่ดีกว่าให้ตอบ แต่คำตอบที่ฉันให้ไว้เน้นวิธีการใช้คลาสที่มีเอกสารในลักษณะเดียวกับคลาสการทำให้เป็นอนุกรมอื่น ๆ ที่ใช้รหัสนี้
Jason Jackson

21
@JSBangs FormatterServices ( msdn.microsoft.com/en-us/library/… ) ไม่เป็นเอกสาร
Autodidact


72

ใช้เมธอด CreateInstance ที่มากเกินไป:

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

สร้างอินสแตนซ์ของชนิดที่ระบุโดยใช้ตัวสร้างที่ตรงกับพารามิเตอร์ที่ระบุมากที่สุด

ดู: http://msdn.microsoft.com/en-us/library/wcxyzt4d.aspx


1
โซลูชันนี้ช่วยลดความซับซ้อนของปัญหา จะเกิดอะไรขึ้นถ้าฉันไม่ทราบประเภทของตัวเองและกำลังพูดว่า "just create an object of the Type in this Type variable"?
kamii

23

เมื่อฉันเปรียบเทียบประสิทธิภาพของ(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)จะล้มเหลวสำหรับสตริง ดังนั้นการจัดการพิเศษสำหรับสตริงจึงถูกนำมาใช้เพื่อส่งคืนสตริงว่าง


1
เป็นเรื่องแปลกที่การดูรหัสของใครสักคนหนึ่งบรรทัดสามารถช่วยประหยัดเวลาได้หนึ่งวัน ขอบคุณครับ! เหตุผลด้านประสิทธิภาพพาฉันไปที่โพสต์ของคุณและเคล็ดลับก็เสร็จแล้ว :) คลาส FormatterServices และ Activator มีประสิทธิภาพต่ำกว่าเมื่อเทียบกับนิพจน์ที่รวบรวมสิ่งที่น่าเสียดายที่พบว่า Activators อยู่ทั่วสถานที่
jmodrak

@nawfal เกี่ยวกับการจัดการสตริงพิเศษของคุณฉันรู้ว่ามันจะล้มเหลวสำหรับสตริงที่ไม่มีการจัดการพิเศษนี้ แต่ฉันแค่อยากรู้ว่ามันจะใช้ได้กับทุกประเภทหรือไม่
Sнаđошƒаӽ

@ Sнаđошƒаӽน่าเสียดายที่ไม่มี ตัวอย่างที่ให้มาคือแบร์โบนและ. NET มีหลายประเภทหลายประเภท เช่นลองพิจารณาว่าถ้าคุณผ่านประเภทผู้รับมอบสิทธิ์คุณจะให้อินสแตนซ์นี้แก่คุณอย่างไร? หรืออื่น ๆ ถ้าตัวสร้างพ่นคุณสามารถทำอะไรได้บ้าง? วิธีต่างๆมากมายในการจัดการ ฉันได้ตอบรับการอัปเดตนี้เพื่อจัดการกับสถานการณ์อื่น ๆ ในห้องสมุดของฉัน ตอนนี้ยังไม่เผยแพร่ที่ไหน
nawfal

4

คำตอบที่ดี แต่ใช้ไม่ได้ในกรอบงาน 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);
    }
}

1
นี่คือวิธีที่ฉันเรียกว่าตัวสร้างที่ไม่ใช่ค่าเริ่มต้น ฉันไม่แน่ใจว่าฉันต้องการสร้างวัตถุโดยไม่เรียกตัวสร้างใด ๆ เลย
Rory MacLeod

2
คุณอาจต้องการสร้างออบเจ็กต์โดยไม่ต้องเรียกคอนสตรัคเตอร์หากคุณกำลังเขียนซีเรียลไลเซอร์แบบกำหนดเอง
Autodidact

1
ใช่นั่นคือสถานการณ์การใช้งานที่แน่นอนคำถามนี้มีไว้สำหรับ :)
Aistina

1
@Aistina บางทีคุณอาจเพิ่มข้อมูลนี้ในคำถาม? คนส่วนใหญ่จะต่อต้านการสร้างวัตถุโดยไม่เรียก ctors ของพวกเขาและจะใช้เวลาในการโต้เถียงกับคุณเกี่ยวกับเรื่องนั้น แต่กรณีการใช้งานของคุณให้เหตุผลว่าเป็นจริงดังนั้นฉันคิดว่ามันเกี่ยวข้องกับคำถามนี้มาก
julealgon
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.