Pergunta

abaixo está um exemplo de código e a pergunta, observe que NÃO posso usar C # 4.0 e a palavra-chave dinâmica.

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!";
        }
    }
}

Também tentei mais alguns métodos usando Delegate.CreateDelegate, mas sem sucesso. Isso é possível?

Editar: não tenho medo de usar um método dinâmico (e montador MSIL), mas meu conhecimento nesta área é muito limitado.

Edit2: Aqui está um exemplo que está mais próximo do que estou realmente tentando fazer:

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


    }

}
Foi útil?

Solução

Atualização 3 : OK, já que você está satisfeito com uma solução feia, convém verificar as palavras-chave __refvalue e __makeref não documentadas .


Parece que seu problema é que você deseja especificar o tipo de parâmetro ref object a ser convertido ou alterado para.

O problema com isso é que você não pode simplesmente atribuir arbitrariamente uma variável de qualquer tipo T a um string, por exemplo. Portanto, você precisa passar um ref object ou ref string para que funcione em tudo .

Parece-me que oberfreak acertou em cheio o que você estava tentando alcançar (originalmente não percebi que você inicializou o como um string e, portanto, queria claramente que seu tipo real influenciasse o comportamento da função Test2). A resposta dele tem a abordagem certa para você.

Atualização : você menciona em um comentário que o que está tentando fazer é ter um comportamento dinâmico que poderia ser alcançado usando um dicionário. Suponho que seja algo assim?

Atualização 2 : atualizou este exemplo com base em seu exemplo atualizado.

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

Isso deve funcionar. Mas você não pode realmente conseguir o mesmo com os genéricos porque não há como fazer isso (como você sabe):

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

Se houvesse , você estaria pronto. Mas a única maneira possível de fazer isso é usando reflexão, que é feia e cara para algo assim e claramente frustraria sua intenção de manter isso simples e garantir um bom desempenho.

Outras dicas

Parece que seu comentário já sabe como fazer:

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

Isso é apenas transformar seu comentário em código.Isso de alguma forma não faz o que você quer?

As principais questões, na minha opinião, são: o que você quer fazer?

Se você deseja apenas atribuir uma string a um objeto de referência, pode tentar isto:

Os genéricos podem ser definidos durante o tempo de execução, mas não é muito confortável e deve ser feito por reflexão ... na minha opinião, é um "não vai", mas apenas uma opinião

Tente usar o object.GetType() para obter o tipo atual do objeto.

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!";
        }
    }
}

A maneira certa de implementar o método Test2 seria

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

Ou estou perdendo alguma coisa aqui?

Se você pode alterar o ref para um retorno normal, pode trapacear massivamente no 4.0 via dynamic:

dynamic foo = obj;
Test(foo);

que agora é:

Test<TheActualTypeOfObj>(obj);

Exemplo completo:

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

que escreve "Olá, mundo!"

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top