Làm thế nào để tôi sử dụng sự phản chiếu để gọi một phương thức chung?


1071

Cách tốt nhất để gọi một phương thức chung khi tham số loại không được biết đến tại thời điểm biên dịch, nhưng thay vào đó được lấy động khi chạy?

Hãy xem xét mã mẫu sau - bên trong Example()phương thức, cách nào ngắn gọn nhất để gọi GenericMethod<T>()bằng cách sử dụng Typeđược lưu trữ trong myTypebiến?

public class Sample
{
    public void Example(string typeName)
    {
        Type myType = FindType(typeName);

        // What goes here to call GenericMethod<T>()?
        GenericMethod<myType>(); // This doesn't work

        // What changes to call StaticMethod<T>()?
        Sample.StaticMethod<myType>(); // This also doesn't work
    }

    public void GenericMethod<T>()
    {
        // ...
    }

    public static void StaticMethod<T>()
    {
        //...
    }
}

7
Tôi đã thử giải pháp của Jon và không thể làm cho nó hoạt động cho đến khi tôi công khai phương thức chung trong lớp. Tôi biết rằng một Jon khác đã trả lời rằng bạn cần chỉ định các ràng buộc nhưng điều này không giúp được gì.
Naskew

12
Bạn cũng cần BindingFlags.Instance, không chỉ BindingFlags.NonPublic, để có được phương thức riêng tư / nội bộ.
Lars Kemmann

2
Phiên bản hiện đại của câu hỏi này: stackoverflow.com/q/2433436/103167
Ben Voigt

@Peter Mortensen - fyi Tôi đã sử dụng khoảng trắng trước dấu '?' để tách các phần tiếng Anh khỏi các phần không phải tiếng Anh (C #); IMHO loại bỏ không gian làm cho nó trông giống như? là một phần của mã. Nếu không có mã, tôi chắc chắn đồng ý xóa các khoảng trắng, nhưng trong trường hợp này ...
Bevan

Câu trả lời:


1138

Bạn cần sử dụng sự phản chiếu để bắt đầu phương thức, sau đó "xây dựng" nó bằng cách cung cấp các đối số kiểu với MakeGenericMethod :

MethodInfo method = typeof(Sample).GetMethod(nameof(Sample.GenericMethod));
MethodInfo generic = method.MakeGenericMethod(myType);
generic.Invoke(this, null);

Đối với một phương thức tĩnh, truyền nulllàm đối số đầu tiên cho Invoke. Điều đó không có gì để làm với các phương pháp chung - đó chỉ là sự phản ánh bình thường.

Như đã lưu ý, rất nhiều điều này đơn giản hơn khi sử dụng C # 4 dynamic- tất nhiên nếu bạn có thể sử dụng suy luận kiểu. Nó không giúp ích trong trường hợp suy luận kiểu không có sẵn, chẳng hạn như ví dụ chính xác trong câu hỏi.


92
+1; lưu ý rằng GetMethod()chỉ xem xét các phương thức cá thể công khai theo mặc định, vì vậy bạn có thể cần BindingFlags.Staticvà / hoặc BindingFlags.NonPublic.

20
Sự kết hợp chính xác của các cờ là BindingFlags.NonPublic | BindingFlags.Instance(và tùy chọn BindingFlags.Static).
Lars Kemmann

4
Một câu hỏi được đánh dấu dupe của điều này tự hỏi làm thế nào để làm điều này với các phương thức tĩnh - và về mặt kỹ thuật thì câu hỏi ở đây cũng vậy. Tham số đầu tiên của generic.Invoke () sẽ là null khi gọi các phương thức tĩnh. Tham số đầu tiên chỉ cần thiết khi gọi các phương thức cá thể.
Chris Moschini

2
@ChrisMoschini: Đã thêm vào câu trả lời.
Jon Skeet

2
@gzou: Tôi đã thêm một cái gì đó vào câu trả lời - nhưng lưu ý rằng để gọi các phương thức chung trong câu hỏi , dynamickhông giúp ích gì vì kiểu suy luận không khả dụng. (Không có đối số mà trình biên dịch có thể sử dụng để xác định đối số loại.)
Jon Skeet

170

Chỉ là một bổ sung cho câu trả lời ban đầu. Trong khi điều này sẽ làm việc:

MethodInfo method = typeof(Sample).GetMethod("GenericMethod");
MethodInfo generic = method.MakeGenericMethod(myType);
generic.Invoke(this, null);

Nó cũng hơi nguy hiểm khi bạn mất kiểm tra thời gian biên dịch GenericMethod. Nếu sau này bạn thực hiện tái cấu trúc và đổi tên GenericMethod, mã này sẽ không thông báo và sẽ thất bại trong thời gian chạy. Ngoài ra, nếu có bất kỳ quá trình xử lý hậu kỳ nào của hội đồng (ví dụ như làm xáo trộn hoặc loại bỏ các phương thức / lớp không sử dụng) thì mã này cũng có thể bị hỏng.

Vì vậy, nếu bạn biết phương thức bạn đang liên kết tại thời điểm biên dịch và điều này không được gọi là hàng triệu lần nên chi phí không thành vấn đề, tôi sẽ thay đổi mã này thành:

Action<> GenMethod = GenericMethod<int>;  //change int by any base type 
                                          //accepted by GenericMethod
MethodInfo method = this.GetType().GetMethod(GenMethod.Method.Name);
MethodInfo generic = method.MakeGenericMethod(myType);
generic.Invoke(this, null);

Mặc dù không đẹp lắm, nhưng bạn có một tham chiếu thời gian biên dịch GenericMethodở đây và nếu bạn cấu trúc lại, xóa hoặc làm bất cứ điều gì với GenericMethod, mã này sẽ tiếp tục hoạt động hoặc ít nhất là phá vỡ tại thời gian biên dịch (ví dụ: nếu bạn xóa GenericMethod).

Một cách khác để làm điều tương tự là tạo một lớp bao bọc mới và tạo nó thông qua Activator. Tôi không biết có cách nào tốt hơn không.


5
Trong trường hợp phản xạ được sử dụng để gọi một phương thức, thông thường tên phương thức được phát hiện bởi một phương thức khác. Biết trước tên phương thức không phổ biến.
Bevan

13
Vâng, tôi đồng ý cho sử dụng phổ biến của sự phản ánh. Nhưng câu hỏi ban đầu là làm thế nào để gọi "GenericMethod <myType> ()" Nếu cú ​​pháp đó được cho phép, chúng ta hoàn toàn không cần GetMethod (). Nhưng đối với câu hỏi "làm cách nào để viết" GenericMethod <myType> "? Tôi nghĩ rằng câu trả lời nên bao gồm một cách để tránh mất liên kết thời gian biên dịch với GenericMethod. Bây giờ nếu câu hỏi này là phổ biến hay không tôi không biết, nhưng tôi không biết, nhưng Tôi biết rằng tôi đã có vấn đề chính xác này ngày hôm qua, và đó là lý do tại sao tôi rơi vào câu hỏi này.
Adrian Gallero

20
Bạn có thể làm GenMethod.Method.GetGenericMethodDefinition()thay vì this.GetType().GetMethod(GenMethod.Method.Name). Nó sạch hơn một chút và có lẽ an toàn hơn.
Daniel Cassidy

"MyType" trong mẫu của bạn có nghĩa là gì?
Nhà phát triển

37
Bây giờ bạn có thể sử dụngnameof(GenericMethod)
dmigo 17/03/2016

140

Gọi một phương thức chung với tham số loại chỉ được biết đến trong thời gian chạy có thể được đơn giản hóa rất nhiều bằng cách sử dụng một dynamicloại thay vì API phản chiếu.

Để sử dụng kỹ thuật này, loại phải được biết từ đối tượng thực tế (không chỉ là một thể hiện của Typelớp). Mặt khác, bạn phải tạo một đối tượng thuộc loại đó hoặc sử dụng giải pháp API phản chiếu tiêu chuẩn . Bạn có thể tạo một đối tượng bằng cách sử dụng phương thức Activator.CreateInstance .

Nếu bạn muốn gọi một phương thức chung, thì trong cách sử dụng "bình thường" sẽ có kiểu được suy ra, thì nó chỉ đơn giản là chuyển đối tượng thuộc loại không xác định sang dynamic. Đây là một ví dụ:

class Alpha { }
class Beta { }
class Service
{
    public void Process<T>(T item)
    {
        Console.WriteLine("item.GetType(): " + item.GetType()
                          + "\ttypeof(T): " + typeof(T));
    }
}

class Program
{
    static void Main(string[] args)
    {
        var a = new Alpha();
        var b = new Beta();

        var service = new Service();
        service.Process(a); // Same as "service.Process<Alpha>(a)"
        service.Process(b); // Same as "service.Process<Beta>(b)"

        var objects = new object[] { a, b };
        foreach (var o in objects)
        {
            service.Process(o); // Same as "service.Process<object>(o)"
        }
        foreach (var o in objects)
        {
            dynamic dynObj = o;
            service.Process(dynObj); // Or write "service.Process((dynamic)o)"
        }
    }
}

Và đây là đầu ra của chương trình này:

item.GetType(): Alpha    typeof(T): Alpha
item.GetType(): Beta     typeof(T): Beta
item.GetType(): Alpha    typeof(T): System.Object
item.GetType(): Beta     typeof(T): System.Object
item.GetType(): Alpha    typeof(T): Alpha
item.GetType(): Beta     typeof(T): Beta

Processlà một phương thức cá thể chung viết kiểu thực của đối số được truyền (bằng cách sử dụng GetType()phương thức) và kiểu của tham số chung (bằng cách sử dụng typeoftoán tử).

Bằng cách chuyển đối số đối tượng thành dynamicloại, chúng tôi đã hoãn cung cấp tham số loại cho đến khi thời gian chạy. Khi Processphương thức được gọi với dynamicđối số thì trình biên dịch không quan tâm đến kiểu của đối số này. Trình biên dịch tạo mã mà trong thời gian chạy kiểm tra các loại đối số được truyền thực sự (bằng cách sử dụng sự phản chiếu) và chọn phương thức tốt nhất để gọi. Ở đây chỉ có một phương thức chung này, vì vậy nó được gọi với một tham số loại thích hợp.

Trong ví dụ này, đầu ra giống như khi bạn viết:

foreach (var o in objects)
{
    MethodInfo method = typeof(Service).GetMethod("Process");
    MethodInfo generic = method.MakeGenericMethod(o.GetType());
    generic.Invoke(service, new object[] { o });
}

Phiên bản với loại động chắc chắn là ngắn hơn và dễ viết hơn. Bạn cũng không nên lo lắng về hiệu suất của việc gọi chức năng này nhiều lần. Cuộc gọi tiếp theo với các đối số cùng loại sẽ nhanh hơn nhờ cơ chế lưu vào bộ đệm trong DLR. Tất nhiên, bạn có thể viết mã mà bộ nhớ cache đã gọi các đại biểu, nhưng bằng cách sử dụng dynamicloại bạn có được hành vi này miễn phí.

Nếu phương thức chung mà bạn muốn gọi không có đối số của kiểu tham số hóa (vì vậy tham số loại của nó không thể được suy ra) thì bạn có thể gói lời gọi của phương thức chung trong phương thức trợ giúp như trong ví dụ sau:

class Program
{
    static void Main(string[] args)
    {
        object obj = new Alpha();

        Helper((dynamic)obj);
    }

    public static void Helper<T>(T obj)
    {
        GenericMethod<T>();
    }

    public static void GenericMethod<T>()
    {
        Console.WriteLine("GenericMethod<" + typeof(T) + ">");
    }
}

Tăng độ an toàn

Điều thực sự tuyệt vời khi sử dụng dynamicđối tượng thay thế cho việc sử dụng API phản chiếu là bạn chỉ mất thời gian biên dịch kiểm tra loại cụ thể này mà bạn không biết cho đến khi chạy. Các đối số khác và tên của phương thức được trình biên dịch phân tích tĩnh như bình thường. Nếu bạn xóa hoặc thêm nhiều đối số, thay đổi loại hoặc đổi tên phương thức thì bạn sẽ gặp lỗi thời gian biên dịch. Điều này sẽ không xảy ra nếu bạn cung cấp tên phương thức dưới dạng một chuỗi trong Type.GetMethodvà đối số là mảng đối tượng MethodInfo.Invoke.

Dưới đây là một ví dụ đơn giản minh họa cách một số lỗi có thể bị bắt tại thời gian biên dịch (mã nhận xét) và lỗi khác khi chạy. Nó cũng cho thấy DLR cố gắng giải quyết phương thức nào sẽ gọi.

interface IItem { }
class FooItem : IItem { }
class BarItem : IItem { }
class Alpha { }

class Program
{
    static void Main(string[] args)
    {
        var objects = new object[] { new FooItem(), new BarItem(), new Alpha() };
        for (int i = 0; i < objects.Length; i++)
        {
            ProcessItem((dynamic)objects[i], "test" + i, i);

            //ProcesItm((dynamic)objects[i], "test" + i, i);
            //compiler error: The name 'ProcesItm' does not
            //exist in the current context

            //ProcessItem((dynamic)objects[i], "test" + i);
            //error: No overload for method 'ProcessItem' takes 2 arguments
        }
    }

    static string ProcessItem<T>(T item, string text, int number)
        where T : IItem
    {
        Console.WriteLine("Generic ProcessItem<{0}>, text {1}, number:{2}",
                          typeof(T), text, number);
        return "OK";
    }
    static void ProcessItem(BarItem item, string text, int number)
    {
        Console.WriteLine("ProcessItem with Bar, " + text + ", " + number);
    }
}

Ở đây chúng ta lại thực hiện một số phương thức bằng cách chuyển đối số thành dynamickiểu. Chỉ xác minh loại đối số đầu tiên được hoãn vào thời gian chạy. Bạn sẽ gặp lỗi trình biên dịch nếu tên của phương thức bạn gọi không tồn tại hoặc nếu các đối số khác không hợp lệ (sai số lượng đối số hoặc loại sai).

Khi bạn truyền dynamicđối số cho một phương thức thì cuộc gọi này gần đây bị ràng buộc . Phương pháp giải quyết quá tải xảy ra trong thời gian chạy và cố gắng chọn quá tải tốt nhất. Vì vậy, nếu bạn gọi ProcessItemphương thức với một đối tượng BarItemkiểu thì bạn thực sự sẽ gọi phương thức không chung chung, bởi vì nó phù hợp hơn với loại này. Tuy nhiên, bạn sẽ gặp lỗi thời gian chạy khi bạn chuyển một đối số của Alphaloại vì không có phương thức nào có thể xử lý đối tượng này (một phương thức chung có ràng buộc where T : IItemAlphalớp không thực hiện giao diện này). Nhưng đó là toàn bộ vấn đề. Trình biên dịch không có thông tin rằng cuộc gọi này là hợp lệ. Bạn là một lập trình viên biết điều này và bạn nên chắc chắn rằng mã này chạy không có lỗi.

Kiểu trả về

Khi bạn gọi một phương pháp phi khoảng trống với một tham số của loại năng động, kiểu trả về của nó có thể sẽ dynamicquá . Vì vậy, nếu bạn thay đổi ví dụ trước thành mã này:

var result = ProcessItem((dynamic)testObjects[i], "test" + i, i);

thì loại đối tượng kết quả sẽ là dynamic. Điều này là do trình biên dịch không luôn biết phương thức nào sẽ được gọi. Nếu bạn biết kiểu trả về của lệnh gọi hàm thì bạn nên ngầm chuyển nó thành kiểu được yêu cầu để phần còn lại của mã được nhập tĩnh:

string result = ProcessItem((dynamic)testObjects[i], "test" + i, i);

Bạn sẽ gặp lỗi thời gian chạy nếu loại không khớp.

Thực tế, nếu bạn cố gắng lấy giá trị kết quả trong ví dụ trước thì bạn sẽ gặp lỗi thời gian chạy trong vòng lặp thứ hai. Điều này là do bạn đã cố lưu giá trị trả về của hàm void.


Mariusz, bối rối bởi "Tuy nhiên, bạn sẽ gặp lỗi thời gian chạy khi bạn chuyển đối số kiểu Alpha vì không có phương thức nào có thể xử lý đối tượng này." Nếu tôi gọi var a = new Alpha () ProcessItem (a, "test" + i , i) Tại sao phương thức ProcessItem chung xử lý việc này một cách hiệu quả, xuất ra "Mục quy trình chung"?
Alex Edelstein

@AlexEdelstein Tôi đã chỉnh sửa câu trả lời của mình để làm rõ một chút. Đó là bởi vì ProcessItemphương thức chung có ràng buộc chung và chỉ chấp nhận đối tượng thực hiện IItemgiao diện. Khi bạn sẽ gọi ProcessItem(new Aplha(), "test" , 1);hoặc ProcessItem((object)(new Aplha()), "test" , 1);bạn sẽ gặp lỗi trình biên dịch nhưng khi truyền cho dynamicbạn hoãn lại việc kiểm tra thời gian chạy.
Mariusz Pawelski

Câu trả lời và giải thích tuyệt vời, hoạt động hoàn hảo cho tôi. Tốt hơn nhiều so với câu trả lời được chấp nhận, viết ngắn hơn, hiệu quả hơn và an toàn hơn.
ygoe

17

Với C # 4.0, sự phản chiếu là không cần thiết vì DLR có thể gọi nó bằng các kiểu thời gian chạy. Vì việc sử dụng thư viện DLR là một nỗi đau một cách linh hoạt (thay vì mã tạo trình biên dịch C # cho bạn), khung công tác mã nguồn mở Dynamitey (.net tiêu chuẩn 1.5) cho phép bạn truy cập thời gian chạy được lưu vào bộ nhớ cache dễ dàng vào cùng các cuộc gọi mà trình biên dịch sẽ tạo ra cho bạn.

var name = InvokeMemberName.Create;
Dynamic.InvokeMemberAction(this, name("GenericMethod", new[]{myType}));


var staticContext = InvokeContext.CreateStatic;
Dynamic.InvokeMemberAction(staticContext(typeof(Sample)), name("StaticMethod", new[]{myType}));

13

Thêm vào câu trả lời của Adrian Gallero :

Gọi một phương thức chung từ thông tin loại bao gồm ba bước.

TLDR: Gọi một phương thức chung đã biết với một đối tượng kiểu có thể được thực hiện bằng cách:

((Action)GenericMethod<object>)
    .Method
    .GetGenericMethodDefinition()
    .MakeGenericMethod(typeof(string))
    .Invoke(this, null);

trong đó GenericMethod<object>tên phương thức để gọi và bất kỳ loại nào thỏa mãn các ràng buộc chung.

(Hành động) khớp với chữ ký của phương thức được gọi là ( Func<string,string,int>hoặc Action<bool>)

Bước 1 là lấy MethodInfo cho định nghĩa phương thức chung

Phương pháp 1: Sử dụng GetMethod () hoặc GetMethods () với các loại cờ hoặc ràng buộc thích hợp.

MethodInfo method = typeof(Sample).GetMethod("GenericMethod");

Phương pháp 2: Tạo một ủy nhiệm, lấy đối tượng MethodInfo và sau đó gọi GetGenericMethodDefDef

Từ bên trong lớp có chứa các phương thức:

MethodInfo method = ((Action)GenericMethod<object>)
    .Method
    .GetGenericMethodDefinition();

MethodInfo method = ((Action)StaticMethod<object>)
    .Method
    .GetGenericMethodDefinition();

Từ bên ngoài lớp có chứa các phương thức:

MethodInfo method = ((Action)(new Sample())
    .GenericMethod<object>)
    .Method
    .GetGenericMethodDefinition();

MethodInfo method = ((Action)Sample.StaticMethod<object>)
    .Method
    .GetGenericMethodDefinition();

Trong C #, tên của một phương thức, tức là "ToString" hoặc "GenericMethod" thực sự đề cập đến một nhóm các phương thức có thể chứa một hoặc nhiều phương thức. Cho đến khi bạn cung cấp các loại tham số phương thức, bạn không biết phương thức nào bạn đang đề cập đến.

((Action)GenericMethod<object>)đề cập đến các đại biểu cho một phương pháp cụ thể. ((Func<string, int>)GenericMethod<object>) đề cập đến sự quá tải khác nhau của GenericMethod

Phương pháp 3: Tạo biểu thức lambda chứa biểu thức gọi phương thức, lấy đối tượng MethodInfo và sau đó GetGenericMethodDefDef

MethodInfo method = ((MethodCallExpression)((Expression<Action<Sample>>)(
    (Sample v) => v.GenericMethod<object>()
    )).Body).Method.GetGenericMethodDefinition();

Điều này phá vỡ để

Tạo một biểu thức lambda trong đó cơ thể là một cuộc gọi đến phương thức bạn muốn.

Expression<Action<Sample>> expr = (Sample v) => v.GenericMethod<object>();

Trích xuất phần thân và chuyển sang Phương thứcCallExpression

MethodCallExpression methodCallExpr = (MethodCallExpression)expr.Body;

Lấy định nghĩa phương thức chung từ phương thức

MethodInfo methodA = methodCallExpr.Method.GetGenericMethodDefinition();

Bước 2 đang gọi MakeGenericMethod để tạo một phương thức chung với (các) loại thích hợp.

MethodInfo generic = method.MakeGenericMethod(myType);

Bước 3 là gọi phương thức với các đối số thích hợp.

generic.Invoke(this, null);

8

Không ai cung cấp giải pháp " Phản xạ cổ điển ", vì vậy đây là một ví dụ mã hoàn chỉnh:

using System;
using System.Collections;
using System.Collections.Generic;

namespace DictionaryRuntime
{
    public class DynamicDictionaryFactory
    {
        /// <summary>
        /// Factory to create dynamically a generic Dictionary.
        /// </summary>
        public IDictionary CreateDynamicGenericInstance(Type keyType, Type valueType)
        {
            //Creating the Dictionary.
            Type typeDict = typeof(Dictionary<,>);

            //Creating KeyValue Type for Dictionary.
            Type[] typeArgs = { keyType, valueType };

            //Passing the Type and create Dictionary Type.
            Type genericType = typeDict.MakeGenericType(typeArgs);

            //Creating Instance for Dictionary<K,T>.
            IDictionary d = Activator.CreateInstance(genericType) as IDictionary;

            return d;

        }
    }
}

Lớp trên DynamicDictionaryFactorycó một phương thức

CreateDynamicGenericInstance(Type keyType, Type valueType)

và nó tạo và trả về một thể hiện IDadata, các loại có khóa và giá trị chính xác được chỉ định trong cuộc gọi keyTypevalueType.

Dưới đây là một ví dụ đầy đủ về cách gọi phương thức này để khởi tạo và sử dụng Dictionary<String, int>:

using System;
using System.Collections.Generic;

namespace DynamicDictionary
{
    class Test
    {
        static void Main(string[] args)
        {
            var factory = new DictionaryRuntime.DynamicDictionaryFactory();
            var dict = factory.CreateDynamicGenericInstance(typeof(String), typeof(int));

            var typedDict = dict as Dictionary<String, int>;

            if (typedDict != null)
            {
                Console.WriteLine("Dictionary<String, int>");

                typedDict.Add("One", 1);
                typedDict.Add("Two", 2);
                typedDict.Add("Three", 3);

                foreach(var kvp in typedDict)
                {
                    Console.WriteLine("\"" + kvp.Key + "\": " + kvp.Value);
                }
            }
            else
                Console.WriteLine("null");
        }
    }
}

Khi ứng dụng bảng điều khiển trên được thực thi, chúng ta sẽ nhận được kết quả đúng như mong đợi:

Dictionary<String, int>
"One": 1
"Two": 2
"Three": 3

2

Đây là 2 xu của tôi dựa trên câu trả lời của Grax , nhưng với hai tham số cần thiết cho một phương thức chung.

Giả sử phương thức của bạn được định nghĩa như sau trong lớp Người trợ giúp:

public class Helpers
{
    public static U ConvertCsvDataToCollection<U, T>(string csvData)
    where U : ObservableCollection<T>
    {
      //transform code here
    }
}

Trong trường hợp của tôi, loại U luôn là một đối tượng lưu trữ bộ sưu tập có thể quan sát được của loại T.

Vì tôi đã xác định trước các loại của mình, trước tiên tôi tạo các đối tượng "giả" đại diện cho bộ sưu tập có thể quan sát được (U) và đối tượng được lưu trữ trong đó (T) và sẽ được sử dụng bên dưới để lấy loại của chúng khi gọi Make

object myCollection = Activator.CreateInstance(collectionType);
object myoObject = Activator.CreateInstance(objectType);

Sau đó gọi GetMethod để tìm chức năng Chung của bạn:

MethodInfo method = typeof(Helpers).
GetMethod("ConvertCsvDataToCollection");

Cho đến nay, cuộc gọi trên khá giống với những gì đã được giải thích ở trên nhưng với một sự khác biệt nhỏ khi bạn cần phải truyền nhiều tham số cho nó.

Bạn cần chuyển một mảng Loại [] cho hàm MakeGenericMethod có chứa các loại đối tượng "giả" được tạo ở trên:

MethodInfo generic = method.MakeGenericMethod(
new Type[] {
   myCollection.GetType(),
   myObject.GetType()
});

Khi đã xong, bạn cần gọi phương thức Gọi như đã đề cập ở trên.

generic.Invoke(null, new object[] { csvData });

Và bạn đã hoàn thành. Hoạt động một sự quyến rũ!

CẬP NHẬT:

Như @Bevan nhấn mạnh, tôi không cần tạo một mảng khi gọi hàm MakeGenericMethod vì nó cần các tham số và tôi không cần tạo một đối tượng để có được các kiểu vì tôi chỉ có thể chuyển các loại trực tiếp cho hàm này. Trong trường hợp của tôi, vì tôi có các loại được xác định trước trong một lớp khác, tôi chỉ cần thay đổi mã của mình thành:

object myCollection = null;

MethodInfo method = typeof(Helpers).
GetMethod("ConvertCsvDataToCollection");

MethodInfo generic = method.MakeGenericMethod(
   myClassInfo.CollectionType,
   myClassInfo.ObjectType
);

myCollection = generic.Invoke(null, new object[] { csvData });

myClassInfo chứa 2 thuộc tính loại Typemà tôi đặt trong thời gian chạy dựa trên giá trị enum được truyền cho hàm tạo và sẽ cung cấp cho tôi các loại có liên quan mà sau đó tôi sử dụng trong MakeGenericMethod.

Cảm ơn một lần nữa vì đã làm nổi bật @Bevan này.


Các đối số để MakeGenericMethod()có từ khóa params, do đó bạn không cần phải tạo một mảng; Bạn cũng không cần phải tạo các thể hiện để có được các loại - methodInfo.MakeGenericMethod(typeof(TCollection), typeof(TObject))sẽ là đủ.
Bevan

0

Lấy cảm hứng từ câu trả lời của Enigmativity - giả sử bạn có hai (hoặc nhiều) lớp, như

public class Bar { }
public class Square { }

và bạn muốn gọi phương thức Foo<T>với BarSquare, được khai báo là

public class myClass
{
    public void Foo<T>(T item)
    {
        Console.WriteLine(typeof(T).Name);
    }
}

Sau đó, bạn có thể thực hiện một phương thức mở rộng như:

public static class Extension
{
    public static void InvokeFoo<T>(this T t)
    {
        var fooMethod = typeof(myClass).GetMethod("Foo");
        var tType = typeof(T);
        var fooTMethod = fooMethod.MakeGenericMethod(new[] { tType });
        fooTMethod.Invoke(new myClass(), new object[] { t });
    }
}

Với điều này, bạn có thể chỉ cần gọi Foonhư sau:

var objSquare = new Square();
objSquare.InvokeFoo();

var objBar = new Bar();
objBar.InvokeFoo();

mà làm việc cho mọi lớp. Trong trường hợp này, nó sẽ xuất ra:


Thanh vuông

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.