7

Consider the following piece of code:

class MyClass
{
}

class MyClass2 : MyClass
{
}

private void Foo(MyClass cl)
{
    //cl is actually MyClass2 instance
    TestGeneric(cl);
}

private void TestGeneric<T>(T val)
{
     //do smth
}

After calling Foo(), the T in TestGeneric is MyClass, not MyClass2. How do I achieve treating val as a MyClass2 instance? Thanks in advance.

Upd: I don't actually know that the object has been created using MyClass2 ctor, but rather can infer this by calling val.GetType() so a simple as MyClass2 won't work

Gena Verdel
  • 588
  • 5
  • 21
  • 1
    Why are you including the `as` cast at all? – Shmiddty Nov 29 '12 at 08:41
  • Could you give us more details about what do you want to get with this code? – Kirill Bestemyanov Nov 29 '12 at 08:50
  • Here's the explanation: I receive an object that is transferred as an instance of MyClass while it's actually an instance of MyClass2. private void HandleSomeMessage(MyClass cl) I'd like to be able to call a generic method void HandleMessageGeneric(T cl) with the ACTUAL cl's type – Gena Verdel Nov 29 '12 at 08:53
  • Why do you want to call a generic method? It won't make it any easier to write code specific to the actual type of `MyClass` being passed. – Rawling Nov 29 '12 at 10:06

7 Answers7

5

It can be done with a visitor pattern. It is a nice object oriented approach, when you have all handling code in a single handler class (not in each message) and if more message types will be needed, just add additional handler methods.

// Your message classes
public class MyClass : IMessage
{
    // Implement acceptance of handler:
    public void AcceptHandler(IMessageHandler handler)
    {
        handler.HandleMessage(this);
    }
}

public class MyClass2 : MyClass
{
     // Nothing more here
}

// Define interface of message
public interface IMessage
{
    void AcceptHandler(IMessageHandler handler)
}

// Define interface of handler
public interface IMessageHandler
{
    // For each type of message, define separate method
    void HandleMessage(MyClass message)
    void HandleMessage(MyClass2 message)
}

// Implemente actual handler implementation
public class MessageHandler : IMessageHandler 
{
    // Main handler method
    public void HandleSomeMessage(MyClass message) // Or it could be IMessage
    {
         // Pass this handler to message. Since message implements AcceptHandler
         // as just passing itself to handler, correct method of handler for MyClass
         // or MyClass2 will be called at runtime.
         message.AcceptHandler(this);
    }

    public void HandleMessage(MyClass message)
    {
         // Implement what do you need to be done for MyClass
    }

    public void HandleMessage(MyClass2 message)
    {
         // Implement what do you need to be done for MyClass2
         // If code of MyClass should be run too, just call 
         // this.HandleMessage((MyClass)message);
    }
}
Algirdas
  • 677
  • 1
  • 6
  • 15
  • To my best understanding, the aforementioned approach is ultimate match for the case of shared instance of the IMessageHandler interface. However, this is not the case, as each class that has this Dependency injected into it, must resolve a new instance with arbitrary number of messages to handle. That was the intention behind converting the current implementation into a generic one. – Gena Verdel Dec 01 '12 at 16:49
1

Assuming you can change Foo, but not its signature, you could do this:

private void Foo(MyClass cl)
{
    TestGeneric((dynamic)cl);
}

This will resolve the version of TestGeneric that gets called at runtime instead of at compile time, calling TestGeneric<MyClass2> when cl is of that type.

Matthew Strawbridge
  • 19,940
  • 10
  • 72
  • 93
0

Well when you call a generic method, the type parameters will be resolved based on the types of the variables, and not based on the types of the actual values.

So for example if you have:

var x = int as object;
Foo(x);

and then you have this:

void Foo<T>(T value)
{
}

Then the type of T will be object and not int, because that's the type of the variable.

A possible solution would be to dynamically cast the value to the lowest subclass, using either reflection or a compiled expression.

Some other alternatives you have are to use reflection to check the actual type of the value that was passed and base your logic on that, or use other language mechanics such as virtual methods.

If you describe the scenario you are trying to solve, someone could probably suggest a suitable solution.

Ran
  • 5,989
  • 1
  • 24
  • 26
0

Best solution would be to change Foo method to be generic too, so that you can save type information. You should do this like so:

private void Foo<T>(T cl) where T : MyClass
{
    TestGeneric(cl);
}

Otherwise, you would have an example of bad design. Simple way out would be

private void Foo(MyClass cl)
{
    if (cl is MyClass2)
        TestGeneric((MyClass2)cl);
    else
        TestGeneric(cl);
}

You could also do a broader solution using reflection, but that would be abuse of tools to patch bad design.

Following would be a reflection based solution, but I did not run it so bear with me and try to fix possible errors.

private void Foo(MyClass cl)
{
    Type genMethodType = typeof(TestGenericMethodClass);
    MethodInfo genMethod = genMethodType.GetMethod("TestGeneric");
    MethodInfo methodConstructed = genMethod.MakeGenericMethod(cl.GetType());
    object[] args = new object[] { cl };
    methodConstructed.Invoke(instanceOfTestGenericMethodClass, args);
}

So

  1. Get type of class in which your TestGeneric method is defined
  2. Use Type.GetMethod to retrieve method definition
  3. Retrieve actual type of your cl variable to construct generic method
  4. Use MethodInfo.MakeGenericMethod to construct a method for specific type
  5. Use MethodBase.Invoke to invoke constructed method

Your code will differ based on your current implementation (depending on type names, method accessibility and such).

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
Nikola Radosavljević
  • 6,871
  • 32
  • 44
  • The Foo() method's signature may not be changed. It IS a BAD design, but, sadly, I'm not allowed to modify it... Therefore I need to come up with an "Adapter". Your other suggestion is the one I'm trying to avoid. Any other suggestions? – Gena Verdel Nov 29 '12 at 09:10
  • Other solution would be to use reflection. It's uglier but more generic than second solution. I see no other meaningful way than these 3. – Nikola Radosavljević Nov 29 '12 at 09:53
0

(Answering the question from the comment)

It's a bulky solution, and you are depending on concrete implementations, but you could do something along these lines:

//initialization
Dictionary<Type, Action> typeActions = new Dictionary<Type, Action>();
typeActions.Add(typeof (MyClass), () => {Console.WriteLine("MyClass");});
typeActions.Add(typeof (MyClass2), () => {Console.WriteLine("MyClass2");});

private void TestGeneric<T>(T val)
{
   //here some error checking should be in place, 
   //to make sure that T is a valid entry class
   Action action = typeActions[val.GetType()];
   action();
}

A downside to this approach is that it depends on the variables being exactly of type MyClass or MyClass2, so if someone later adds another level of inheritance, this will break, but still, it's more flexible than a if-else or a switch in the generic method.

SWeko
  • 30,434
  • 10
  • 71
  • 106
  • The ultimate goal is to be able to pass an instance of MyClass2 to the generic method, and not to resolve its type inside the method... – Gena Verdel Nov 29 '12 at 09:26
  • The generic call resolving is done on the type of the variable, not on the type of the instance referenced by the variable, so I think you might be out of luck there. – SWeko Nov 29 '12 at 09:30
  • @GenaVerdel you will need to go dynamic to solve that. See the third option of my answer (http://stackoverflow.com/a/13622650/112407) – Rune FS Nov 29 '12 at 13:27
0

A cast is when you know more about the type of an object than the compiler is able to deduce from the static code.

This leaves you with two options when ever you need more type information that you currently have.

  • Change the declaration to make the information explicit at declaration
  • Make a cast

or go dynamic

In your case the first would require changing Foo to be generic too

private void Foo<T>(T cl)
{
    //cl is actually MyClass2 instance
    TestGeneric(cl);
}

The second option would require casting and I'm guessing you have multiple types so you'll need a lot of if-else-if's which is generally a bad sign especially when the condition is based on the type of an object

private void Foo(MyClass cl)
{
    var mc2 = tcl as MyClass2;
    if(mc2 != null) {
        TestGeneric(mc2);
        return;
    }
    var mc3 = tcl as MyClass3;
    if(mc3 != null) {
        TestGeneric(mc3);
        return;
    }
    throw new InvalidOperationException("Type not recognised");
}

lastly you could go dynamic

private void TestDynamic(dynamic val)
{
    TestGeneric(val);
}

There are other ways of doing it dynamically such as runtime generating code but it's a lot easier to simply use the DLR than trying to role your own on

Rune FS
  • 21,497
  • 7
  • 62
  • 96
0

You don't want to call a generic method here. Once you enter TestGeneric<T>, even if T is MyClass2 as you want, you can't have written any code against MyClass2 (or even MyClass, unless you add a restriction on T) so it doesn't help!

You certainly don't need to go down the route of reflection or dynamic.

Most obvious way to do this: put the class-specific behaviour in the class itself:

class MyClass
{
    public virtual void Test()
    {
        // Behaviour for MyClass
    }
}

class MyClass2 : MyClass
{
    public override void Test()
    {
        // Behaviour for MyClass2
    }
}

private void Foo(MyClass cl)
{
    cl.Test();
}

Next-best: branch code depending on the type passed:

private void Foo(MyClass cl)
{
    if (cl is MyClass2)
    {
        Test((MyClass2)cl);
    }
    else
    {
        Test(cl);
    }
}

private void Test(MyClass cl)
{
    // Behaviour for MyClass
}

private void Test(MyClass2 cl2)
{
    // Behaviour for MyClass2
}

In both these cases you can write code directly against MyClass2 (or MyClass) without having to do any reflection, use dynamic, or... whatever you were planning to do in your generic method - branch on typeof(T)?

Rawling
  • 49,248
  • 7
  • 89
  • 127