使用 System.Type 中的泛型类型调用泛型方法

发布于 2024-12-18 23:56:15 字数 2109 浏览 4 评论 0原文

下面是代码示例和问题,请注意我不能使用 C# 4.0 和动态关键字。

static class TestClass
{
    static void Main(string[] args)
    {
        Object o = "Previous value";
        Test(ref o);
        Trace.WriteLine(o);
    }

    static public void Test<T>(ref T obj)
    {
        //  The goal is to somehow invoke Test2 with the real type of obj, i.e the type in obj.GetType() 

        //  1st try:
        Test2(ref obj); // This doesn't work because the type in Test2 will be the same as T here.

        //  2nd try:
        MethodInfo mi = typeof(TestClass).GetMethod("Test2");
        mi = mi.MakeGenericMethod(new Type[] { obj.GetType() });
        mi.Invoke(null, new Object[] { obj }); // obj is no longer by reference so we need to store the object array and copy back the result after the call

        //  3rd try, successful implementation by the smartest mind of stack overflow :)
    }

    static public void Test2<T>(ref T s)
    {
        if (typeof(T) == typeof(String))
        {
            s = (T)(Object)"Hello world!";
        }
    }
}

我还使用 Delegate.CreateDelegate 尝试了更多方法,但没有任何运气。 这有可能吗?

编辑:我并不害怕使用动态方法(和 MSIL 汇编器),但我在这方面的知识非常有限。

Edit2:这是一个更接近我真正想做的事情的例子:

public static class TypeHandler<T>
{
    public delegate void ProcessDelegate(ref T value);

    public static readonly ProcessDelegate Process = Init();

    private static ProcessDelegate Init()
    {
        //  Do lot's of magic stuff and returns a suitable delegate depending on the type
        return null;
    }
}


static class TestClass
{
    static public void Main(string[] args)
    {
        Object o = "Previous value";
        Test(ref o);
        Trace.WriteLine(o);
    }

    static public void Test<T>(ref T obj)
    {
        if (obj is T)
        {
        //  Optimized, common case
            TypeHandler<T>.Process(ref obj);
            return;
        }
        Type t = obj.GetType();
        //  How to call the delegate found in TypeHandler<t>.Process ? (I can get delegate but I can't call it).


    }

}

below is a code example and the question, please note that I can NOT use C# 4.0 and the dynamic keyword.

static class TestClass
{
    static void Main(string[] args)
    {
        Object o = "Previous value";
        Test(ref o);
        Trace.WriteLine(o);
    }

    static public void Test<T>(ref T obj)
    {
        //  The goal is to somehow invoke Test2 with the real type of obj, i.e the type in obj.GetType() 

        //  1st try:
        Test2(ref obj); // This doesn't work because the type in Test2 will be the same as T here.

        //  2nd try:
        MethodInfo mi = typeof(TestClass).GetMethod("Test2");
        mi = mi.MakeGenericMethod(new Type[] { obj.GetType() });
        mi.Invoke(null, new Object[] { obj }); // obj is no longer by reference so we need to store the object array and copy back the result after the call

        //  3rd try, successful implementation by the smartest mind of stack overflow :)
    }

    static public void Test2<T>(ref T s)
    {
        if (typeof(T) == typeof(String))
        {
            s = (T)(Object)"Hello world!";
        }
    }
}

I also tried some more methods using Delegate.CreateDelegate but without any luck.
Is this at all possible?

Edit: I'm not afraid of using a dynamic method (and MSIL assembler) but my knowledge in this area is very limited.

Edit2: Here's an example that is closer to what I really trying to do:

public static class TypeHandler<T>
{
    public delegate void ProcessDelegate(ref T value);

    public static readonly ProcessDelegate Process = Init();

    private static ProcessDelegate Init()
    {
        //  Do lot's of magic stuff and returns a suitable delegate depending on the type
        return null;
    }
}


static class TestClass
{
    static public void Main(string[] args)
    {
        Object o = "Previous value";
        Test(ref o);
        Trace.WriteLine(o);
    }

    static public void Test<T>(ref T obj)
    {
        if (obj is T)
        {
        //  Optimized, common case
            TypeHandler<T>.Process(ref obj);
            return;
        }
        Type t = obj.GetType();
        //  How to call the delegate found in TypeHandler<t>.Process ? (I can get delegate but I can't call it).


    }

}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

夜司空 2024-12-25 23:56:15

您的评论看起来您已经了解如何做到这一点:

MethodInfo mi = typeof(TestClass).GetMethod("Test2");
mi = mi.MakeGenericMethod(new Type[] { obj.GetType() });
object[] args = new object[] { obj };
mi.Invoke(null, args);
obj = (T) args[0];

这实际上只是将您的评论变成代码。这是否不符合您的要求?

Your comment looks like you already understand how to do it:

MethodInfo mi = typeof(TestClass).GetMethod("Test2");
mi = mi.MakeGenericMethod(new Type[] { obj.GetType() });
object[] args = new object[] { obj };
mi.Invoke(null, args);
obj = (T) args[0];

That's really just turning your comment into code. Does that somehow not do what you want?

苏大泽ㄣ 2024-12-25 23:56:15

我认为主要问题是,你想做什么?

如果您只想将字符串分配给引用对象,您可以尝试以下操作:

泛型可以在运行时定义,但它不是很舒服,必须通过反射来完成......在我看来,这是“不行”,但只是一个意见

尝试使用object.GetType()来获取对象的当前类型。

static class TestClass {
    static void Main(string[] args) {
        Object o = "Previous value";
        Test(ref o);
        Console.WriteLine(o);
        Console.ReadLine();
    }

    static public void Test<T>(ref T obj) {
        Object o = (Object)obj;
        Test2(ref o);
        obj = (T)o;
    }

    static public void Test2(ref object s) {
        if (s.GetType().Equals(typeof(String))) {
            s = "Hello world!";
        }
    }
}

The main questions, in my opinion is, what do you want to do?

If you just want to assign a string to a reference object you could try this:

Generics can be defined during runtime, but it's not verry comfortable and has to be done by reflection ... in my oppinion it is a "no go", but just an oppinion

Try using the object.GetType() to get the current Type of the Object.

static class TestClass {
    static void Main(string[] args) {
        Object o = "Previous value";
        Test(ref o);
        Console.WriteLine(o);
        Console.ReadLine();
    }

    static public void Test<T>(ref T obj) {
        Object o = (Object)obj;
        Test2(ref o);
        obj = (T)o;
    }

    static public void Test2(ref object s) {
        if (s.GetType().Equals(typeof(String))) {
            s = "Hello world!";
        }
    }
}
变身佩奇 2024-12-25 23:56:15

更新 3:好的,既然您可以接受丑陋的解决方案,您可能需要查看未记录的解决方案__refvalue__makeref 关键字


您的问题似乎是您希望能够指定要转换更改ref object 参数的类型。

这样做的问题是,例如,您不能随意将任何类型 T 的变量分配给 string。因此,您需要传入一个ref 对象ref string 才能正常工作。

在我看来, oberfreak 明确了你想要实现的目标(我最初没有注意到你已经初始化了 o 作为一个字符串,因此显然希望其实际类型影响Test2函数的行为)。他的回答对你来说是正确的方法。

更新:您在评论中提到您想要做的是具有可以使用字典来实现的动态行为。我猜看起来是这样的?

更新 2:根据您的更新示例更新了示例。

public static class TypeHandler // note: get rid of generic T parameter
{
    delegate void ProcessDelegate(ref object obj); // again, not generic

    static Dictionary<Type, ProcessDelegate> processors = new Dictionary<Type, ProcessDelegate>()
    {
        { typeof(string), (ref object obj) => { obj = "Hello, world!"; } }
        // etc.
    };

    public static void Process(ref object obj)
    {
        processors[obj.GetType()].Invoke(ref obj);
    }
}

应该可以。但你不能用泛型真正实现同样的目标,因为没有办法做到这一点(如你所知):

//          not allowed
//               |
//          -----------
//         |           |
TypeHandler<o.GetType()>.Process(ref o);

如果有,那么你就万事大吉了。但唯一可能的方法是使用反射,对于这样的事情来说,反射是丑陋且昂贵的,并且显然会违背您保持简单并确保良好性能的意图。

Update 3: OK, since you're fine with an ugly solution, you may want to check out the undocumented __refvalue and __makeref keywords.


It seems your issue is that you want to be able to specify the type for a ref object parameter to be converted or changed to.

The problem with that is that you can't just arbitrarily assign a variable of any type T to a string, for example. So you'd need to pass in a ref object or ref string for that to work at all.

It looks to me like oberfreak nailed down what you were trying to achieve (I originally failed to notice you had initialized o as a string and thus clearly wanted its actual type to influence the behavior of the Test2 function). His answer has the right approach for you.

Update: you mention in a comment that what you're trying to do is have dynamic behavior which could be achieved using a dictionary. I'm guessing that looks something like this?

Update 2: updated this example based on your updated example.

public static class TypeHandler // note: get rid of generic T parameter
{
    delegate void ProcessDelegate(ref object obj); // again, not generic

    static Dictionary<Type, ProcessDelegate> processors = new Dictionary<Type, ProcessDelegate>()
    {
        { typeof(string), (ref object obj) => { obj = "Hello, world!"; } }
        // etc.
    };

    public static void Process(ref object obj)
    {
        processors[obj.GetType()].Invoke(ref obj);
    }
}

That should work. But you can't really achieve the same thing with generics because there's no way to do this (as you know):

//          not allowed
//               |
//          -----------
//         |           |
TypeHandler<o.GetType()>.Process(ref o);

If there were, then you'd be all set. But the only possible way you can do that is by using reflection, which is ugly and expensive for something like this and would clearly defeat your intention of keeping this simple and ensuring good performance.

情仇皆在手 2024-12-25 23:56:15

实现方法 Test2 的正确方法是

static public void Test2<T>(ref T s)
    {
        if (s is string)
        {
            s = (T)(Object)"Hello world!";
        }
}

或者我在这里遗漏了一些东西?

The right way to implement method Test2 would be

static public void Test2<T>(ref T s)
    {
        if (s is string)
        {
            s = (T)(Object)"Hello world!";
        }
}

Or am I missing something here?

还如梦归 2024-12-25 23:56:15

如果您可以将 ref 更改为常规返回,则可以通过 dynamic 在 4.0 中进行大规模作弊:

dynamic foo = obj;
Test(foo);

现在是:

Test<TheActualTypeOfObj>(obj);

完整示例:

static void Main(string[] args)
{
    object o = "Previous value";
    o = Test2((dynamic)o);
    Trace.WriteLine(o);
}

static public T Test2<T>(T s)
{
    if (typeof(T) == typeof(string))
    {
        s = (T)(object)"Hello world!";
    }
    return s;
}

其中写入“Hello world!”

If you can change the ref to a regular return, you can cheat massively in 4.0 via dynamic:

dynamic foo = obj;
Test(foo);

that is now:

Test<TheActualTypeOfObj>(obj);

Full example:

static void Main(string[] args)
{
    object o = "Previous value";
    o = Test2((dynamic)o);
    Trace.WriteLine(o);
}

static public T Test2<T>(T s)
{
    if (typeof(T) == typeof(string))
    {
        s = (T)(object)"Hello world!";
    }
    return s;
}

which writes "Hello world!"

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文