3

下面是一个代码示例和问题,请注意我不能使用 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).


    }

}
4

5 回答 5

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!";
        }
    }
}
于 2011-12-02T14:06:18.337 回答
2

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

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

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

于 2011-12-02T14:06:42.923 回答
1

更新 3:好的,既然你对丑陋的解决方案没意见,你可能想查看undocumented__refvalue__makerefkeywords


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

这样做的问题是,例如,您不能随意将任何类型的变量分配给Ta string。因此,您需要传入 a ref objectorref string才能使其正常工作

在我看来,oberfreak确定了您要实现的目标(我最初没有注意到您已初始化o为 a string,因此显然希望其实际类型影响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);

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

于 2011-12-02T14:11:27.147 回答
0

实现方法 Test2 的正确方法是

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

或者我在这里错过了什么?

于 2011-12-02T14:16:30.083 回答
0

如果您可以将其更改ref为常规回报,则可以通过以下方式在 4.0 中大量作弊dynamic

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!”

于 2011-12-02T14:19:13.470 回答