C # Generic and Method

How to choose a good method (the example below shows 2 options don't work). I used instead of a variable of type Object with IF and IS to do the job, but I try to avoid using Object and boxing / unboxing. So I thought Generic could get the job done, but I'm stuck here.

Here is a small piece of code that illustrates my question:

class Program
{
    static void Main(string[] args)
    {
        Parser p = new Parser();
        ObjectType1 o1 = new ObjectType1();
        p.execute(o1);
        Console.Read();
    }
}

class Parser
{
    public T execute<T>(T obj)
    {
        /*
        if (obj is ObjectType1)
            this.action((ObjectType1)obj);
        else if (obj is ObjectType2)
            this.action((ObjectType2)obj);
        */
        this.action(obj);
        return obj;
    }

    private void action(ObjectType1 objectType1)
    {
        Console.WriteLine("1");
    }

    private void action(ObjectType2 objectType2)
    {
        Console.WriteLine("2");
    }
}


class ObjectType1
{
}

class ObjectType2
{
}

      

Update

I don't need an interface and a class. I'm sorry. I knew that was not the purpose of the question.

Casting with (ObjectType) obj doesn't work, but if you do:

        if (obj is ObjectType1)
            this.action(obj as ObjectType1);
        else if (obj is ObjectType2)
            this.action(obj as ObjectType1);

      

it works ... why?

And ... I cannot overload the execute method for all types, because this method is from the interface. This is why everything needs to be called from this method.

0


source to share


7 replies


No, you cannot do this. Generics don't work like C ++ templates - the generic method only compiles once. The only information the compiler can use to resolve the overload is the information it knows within the generic method, no matter what code is using it.

As an example to illustrate this, here's some code that may not work as you expect:



using System;

class Test
{    
    static void Main()
    {
        string x = "hello";
        string y = string.Copy(x);

        Console.WriteLine(x==y); // Overload used
        Compare(x, y);
    }

    static void Compare<T>(T x, T y) where T : class
    {
        Console.WriteLine(x == y); // Reference comparison
    }
}

      

It's hard to say how best to proceed without knowing more about what you want to do.

+4


source


Have you considered interfaces?

interface IAction
{
   void action();
}

class ObjectType1 : IAction
{
   void action() {
      Console.WriteLine("1");
   }
}

class ObjectType2 : IAction
{
    void action() {
      Console.WriteLine("2");
    }
}

class Parser
{
   public IAction execute(IAction obj)
   {
      obj.action();
      return obj;
   }
}

      



Edited by the OP:

This solution would require changing the entire business entity to use this interface. This is really not a thing (in my situation). And, in a different situation, I always prefer to have a pure BusinessObject that has no non-business interface. In my question, I want a solution that has more to do with the Generic / Object / Delegate method to achieve it. You. This answer will not be accepted.

+4


source


I haven't tried it, but can you do it?

public T execute<T>(T obj)
{
    this.action((T)obj);
    return obj;
}

      

Strike>

(doesn't work as per the comments)

or

public T execute<T>(T obj)
{
    this.action(obj as T);
    return obj;
}

      

(according to comments, works)

+2


source


The Parser class has many private methods called by the execute method depending on the type of the object. It should redirect to a good method.

The compiler will do the job for you. Just use overloads.

class Parser
{
    public ObjectType1 action(ObjectType1 objectType1)
    {
        Console.WriteLine("1");
        return objectType1;
    }
    public ObjectType2 action(ObjectType2 objectType2)
    {
        Console.WriteLine("2");
        return objectType2;
    }
}

class ObjectType1 { }
struct ObjectType2 { }

      

Then called with:

Parser p = new Parser();
p.action(new ObjectType1());
p.action(new ObjectType2());

      

No boxing / unboxing and the appropriate method is called.

+2


source


I know you are concerned about boxing / unboxing, so ValueTypes may be involved here.

public T execute<T>(T obj)   
{        
    this.action(obj);
    return obj;
}

      

Let's assume the action is modifying obj, and also assuming the modification is important to the caller (so you are returning the value back to the caller). This code has a nasty default flaw.

Consider this code:

    public int execute(int obj)   
    {        
        this.action(obj);
        return obj;
    }

    public void action(int obj)
    {
        obj = obj + 1;
    }

      

Called this way.

int x = p.execute(1);

      

x is 1, not 2.

+1


source


IIRC you can use a "where" clause to allow this

public T execute<T>(T obj) where : /* somthing */
{
}

      

I always need google to be mine, so I'll leave it there.

edit: reading some comments. I would not advise naming the type of specific code. Rather put this code in a virtual function and call this. The ringtone can be delayed, but for what auto-termination is done.

Koodos for joshua.ewer to find the man page

0


source


Generators happen at compile time. This is best used when you want the same code to apply to different types. It's not dynamic, so it won't help you switch between methods based on input types.

Overload handling, as in David B's answer, works, but also happens at compile time.

The code in your update does the same. It runs (after careful type checking) and then uses an overload to resolve the method.

I feel like you want to switch methods based on runtime input.

You can get more dynamic behavior if you used Reflection.

        public object execute(object obj) 
        {
            MethodInfo m = typeof(Parser).GetMethod(
                "action", 
                BindingFlags.Instance | BindingFlags.NonPublic, 
                null, 
                new Type[] { obj.GetType() }, 
                null);
            m.Invoke(this, new object[] { obj });
            return obj; 
        } 

      

It might be a little fragile, but it works in this example.

0


source







All Articles