【问题标题】:Invoke generic method with the generic type from a System.Type使用 System.Type 中的泛型类型调用泛型方法
【发布时间】:2012-01-11 13:10:23
【问题描述】:

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

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).


    }

}

【问题讨论】:

  • 听起来你不应该尝试使用泛型。无论如何,您都在使用反射,所以请依靠它
  • 您正在检查泛型参数的类型这一事实是一个危险信号。也许泛型在这里不适合使用。

标签: c# .net generics reflection byref


【解决方案1】:

您的评论看起来您已经了解如何操作:

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

这实际上只是将您的评论转化为代码。这难道不是你想要的吗?

【讨论】:

  • 是的,我确实知道如何做到这一点(这是我今天做事的方式),但这是一个优化练习,我想避免按值复制(对于值类型)。
  • @user1077451:这表明性能对您很重要。你的性能目标是什么,当前的代码能达到什么目标? (我怀疑动态方法可以改善事情,但除非你真的必须这样做,否则我不会去那里。)
  • 好问题!我没有固定的目标,但我当前的工作实现是作为概念证明创建的,它运行良好,但速度很慢。我现在正在重写尝试使用书中的每一个技巧(我的有限的书),以尽可能快地实现相同的目标,如果我能让事情以当前性能的 20% 工作,我会很高兴。在本次迭代中,我还为系统增加了更多的灵活性,因此我并不希望在本次迭代中达到目标。在下一轮中,我将考虑使用动态方法和 MSIL,但我首先想确定基础设施。
  • 即:第一次迭代:每种类型都在单个函数中处理,其中有大量 if / else 语句在某些情况下调用虚函数 + 在某些情况下使用反射。第二次迭代:优化每个初始类型的代码路径(即使用类型到代码查找)。第三次迭代:使用 MSIL 为每个案例重写代码(因为有很多案例需要手动处理)。
  • @user1077451:你目前有多少时间花在这段代码上?
【解决方案2】:

在我看来,主要问题是,你想做什么?

如果你只是想给一个引用对象分配一个字符串,你可以试试这个:

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

尝试使用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!";
        }
    }
}

【讨论】:

  • 这甚至不能编译并且不接近我所需要的,如果我只需要为 Object / String 类型工作,那就足够简单了。我需要它适用于任何类型到实现其他类型的任何类型。
  • @user1077451:如果不使用大量if/else 语句,您打算如何做到这一点?你似乎想用他们根本不擅长的泛型来实现一些东西。 (泛型为各种类型提供相同的行为;你所追求的似乎是不同类型的不同的行为。)
  • 我更新了代码,很抱歉这个错误!它现在可以正确编译。为什么这段代码不能解决你的问题?您可以检查 Test2 中的对象类型并决定分配哪个对象?
  • 我不希望强制 Test 只接受对对象的引用,它应该适用于任何类型,否则我需要复制所有值类型(在对象更改后)。对象 o = anObjectOfAnyType;测试(参考 o); anObjectOfAnyType = o;
  • 是的,但它还会打印“Hello world”吗? ;) 你需要添加一个 obj = (T)(Object)o;在测试方法结束时...
【解决方案3】:

更新 3:好的,既然您可以接受丑陋的解决方案,您可能需要查看 the undocumented __refvalue and __makeref keywords


您的问题似乎是您希望能够将ref object 参数的类型指定为convertedchanged

问题在于,例如,您不能随意将任何类型的变量T 分配给string。因此,您需要传入 ref objectref string 才能使其正常工作

在我看来,oberfreak 确定了您想要实现的目标(我最初没有注意到您已将 o 初始化为 string,因此显然想要它的 实际 类型影响Test2 函数的行为)。他的回答适合你。

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

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

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

如果有,那么你就准备好了。但是,您可以做到这一点的唯一可能方法是使用反射,这对于这样的事情来说既丑陋又昂贵,并且显然会破坏您保持这种简单性并确保良好性能的意图。

【讨论】:

  • 这有点类似于我的想法,因为我的实际问题并不像示例那么简单。我喜欢支持的类型(在Test2中)不像您的示例中那样是静态的,而是类似于“TypeHandler”委托的类型字典,然后我有人注册要处理的类型和委托的类型。问题是我正在尝试使用另一种方法,而不是实际使用字典,即拥有一个静态泛型类,该类具有一个带委托的静态字段(使用静态 init 函数初始化)。我希望看到字典查找的速度有所提高。
  • 但问题是你希望这个类基于变量的 actual 类型以某种方式运行。例如,在这种情况下,ostring。但这不是泛型所基于的,它是 declared 类型。在这种情况下,这意味着object 代表o。所以这就是为什么在我个人看来你用泛型树错了树;我觉得 oberfreak 的回答实际上是在为你指明正确的方向。
  • 是的,我以某种方式同意你的观点,泛型可能不是正确的做事方式。我已经用一个更接近我想要做的例子来更新我的原始问题。
  • 这在概念上是我需要的,问题是我不希望 Test1 接受对对象的引用(单独),它应该接受对任何类型的引用(或者我需要支付副本值类型再次)。我知道如果有一个不需要副本的解决方案,它不会很漂亮,但是嘿,这不是选美比赛,是吗? ;)
  • @user1077451:这不是为了漂亮。这是关于性能。 您根本无法通过引入泛型然后添加大量反射来节省性能。反射开销将绝对相形见通过避免值副本获得的微小性能提升。跨度>
【解决方案4】:

实现方法Test2的正确方法是

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

或者我在这里遗漏了什么?

【讨论】:

    【解决方案5】:

    如果您可以将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!”

    【讨论】:

    • 我知道它在 4.0 中很容易,但正如我提到的,我不能将它用于这个项目:(
    • 确实说他不能使用 dynamic 关键字。
    • @user1077451 好的,没发现 - 抱歉。不过,我将把它留在这里,以防万一它可以帮助遇到类似问题的其他人并访问 4.0
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-02-06
    • 2011-05-18
    • 1970-01-01
    • 2022-01-03
    • 1970-01-01
    • 2012-04-21
    • 1970-01-01
    相关资源
    最近更新 更多