Вызвать универсальный метод с универсальным типом из System.Type - PullRequest
3 голосов
/ 02 декабря 2011

ниже приведен пример кода и вопрос, обратите внимание, что я НЕ могу использовать 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).


    }

}

Ответы [ 5 ]

2 голосов
/ 02 декабря 2011

Ваш комментарий выглядит так, как будто вы уже понимаете, как это сделать:

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

Это действительно просто превращение вашего комментария в код. Разве это не делает то, что вы хотите?

2 голосов
/ 02 декабря 2011

Основные вопросы, на мой взгляд, что вы хотите сделать?

Если вы просто хотите присвоить строку ссылочному объекту, вы можете попробовать это:

Обобщения могут быть определены во время выполнения, но это не очень удобно и должно быть сделано с помощью размышлений ... по моему мнению, это "не пойдет", но просто мнение

Попробуйте использовать 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!";
        }
    }
}
1 голос
/ 02 декабря 2011

Обновление 3 : ОК, так как у вас все в порядке с уродливым решением, вы можете проверить недокументированные __refvalue и __makeref ключевые слова .


Похоже, ваша проблема в том, что вы хотите указать тип ref object параметра, который будет преобразован или изменен в.

Проблема в том, что вы не можете просто произвольно назначить переменную любого типа T, например, string.Таким образом, вам нужно будет передать ref object или ref string, чтобы это сработало на всех .

Мне кажется, что oberfreak прибито к тому, чтовы пытались достичь (я изначально не заметил, что вы инициализировали o как 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);

Если бы было , то вы были бы всезадавать.Но единственный возможный способ сделать это - использовать рефлексию, которая уродлива и дорогостоящая для чего-то подобного, и это явно помешает вашему намерению сохранить эту простоту и обеспечить хорошую производительность.

0 голосов
/ 02 декабря 2011

Если вы можете изменить 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!"

0 голосов
/ 02 декабря 2011

Правильный способ реализации метода Test2 будет

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

Или я что-то здесь упускаю?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...