Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# Generics - Calling generic method from a generic class

I have the following classes and I am trying to call Compare method from ExportFileBaseBL class but I get the error

Cannot implicitly convert type 'Class1' to 'T'. An explicit conversion exists (are you missing a cast?)

public abstract class Class1<T> where T: Class2
{
    public abstract Class1<T> Compare(Class1<T> otherObj);
}

public abstract class Class3<T, U> where T: Class1<U>
                         where U: Class2
{
    public T Compare(T obj1, T obj2)
    {
        if (obj1.Prop1 > obj2.Prop1)
        {
            return obj1.Compare(obj2); // Compiler Error here
        }
        else
        {
            return obj2.Compare(obj1);  // Compiler Error here
        }
    }

}

Shouldn't the type conversion be implicit? Am I missing something?

like image 797
logik6 Avatar asked Feb 02 '11 17:02

logik6


2 Answers

The problem is that your abstract Compare method is defined to accept a parameter of type Class1<T> and return an instance of Class1<T>, not a more specific type than Class1<T>. But this is what your Class3.Compare method is attempting to do: call T.Compare and assume the output will be a T, when in fact you can only be sure it will be a Class1<U>.

To provide a simpler, more comprehensible example, suppose I had this class:

class Parser
{
    public abstract object Parse(string text);
}

class Int32Parser
{
    public int Parse(Parser parser, string text)
    {
        return parser.Parse(text);
    }
}

The above code makes a faulty assumption similar to your own: that parser.Parse will return an int just because int derives from object (just as in your case, T must derive from Class1<U>); in fact, you can only be sure it will return an object.

There are two ways I can see to fix this problem: make Class1<T>.Compare a generic method:

public abstract U Compare<U>(U otherObj) where U : Class1<T>;

...or relax the type specificity of your Class3.Compare method's return value:

public Class1<U> Compare(T obj1, T obj2)
{
    // ...
}

Personally, I would prefer the second unless you absolutely need the first. All these generic type constraints can become very messy and burden you more than you expect when the complexity starts to grow like this.

like image 200
Dan Tao Avatar answered Sep 30 '22 00:09

Dan Tao


Call the method with the parameter type you declaring at your class level.

  return obj1.Compare<T>(obj2); 

You'll need to make the definition of the Compare method generic as well:

public abstract Class1<T> Compare<T>(Class1<T> otherObj); 
like image 45
dexter Avatar answered Sep 29 '22 23:09

dexter