Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trying to utilize combination of generic parameters

Tags:

c#

.net

generics

This is confusing, as I'm getting seemingly contradictive errors.

I'm using generics, constraining T to Something, then constraining U to AnOperation<Something>.

I expected that an object AnOperation<Something> is from now on considered of type U. But, I'm getting errors:

Cannot implicitly convert type 'ConsoleApp1.AnOperation<T>' to 'U'

That's weird. Well, i tried explicitly casting it to U, then I got this error:

Cannot convert type 'ConsoleApp1.AnOperation<T>' to 'U' which also stated Cast is redundant

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
        }
    }

    class MyClass<T, U>
        where T : Something
        where U : AnOperation<Something>
    {
        public U GetAnOperationOfSomething()
        {
            AnOperation<T> anOperation = new AnOperation<T>();

            return anOperation; // Cannot implicitly convert type 'ConsoleApp1.AnOperation<T>' to 'U'

            // return (U)anOperation; // Cannot convert type 'ConsoleApp1.AnOperation<T>' to 'U' also Cast is redundant
        }
    }

    public class Something
    {
    }

    public class AnOperation<T>
        where T : Something
    {
    }

}

What's happening here?

Edit: I'm trying to understand what is the problem in the language level, not looking for a workaround on an actual problem.

like image 952
Sharky Avatar asked Oct 23 '17 07:10

Sharky


1 Answers

You almost got it right with your constraint, but not quite. You define

 where U : AnOperation<Something>

But then you create

AnOperation<T> anOperation = new AnOperation<T>()

That is not the same thing. If you change your constraint to...

 where U : AnOperation<T>

...you will be fine.

Another problem is that while every U is an AnOperation<T>, not every AnOperation<T> is an U. When you declare...

public U GetAnOperationOfSomething()

...you are making the guarantee that what the method returns is an U. AnOperation<T> can not satisfy that guarantee.

You are solving this with a typecast to U. That is against the purpose of your generic class, since every U must be an AnOperation<T> or you will get a runtime exception. That makes the whole type parameter U unnecessary. What you actually want to do is create an U. You can use the new() constraint for that:

class MyClass<T, U>
    where T : Something
    where U : AnOperation<T>, new()
{
    public U GetAnOperationOfSomething()
    {
        U anOperation = new U();
        //...
        return anOperation;
    }
}

The new() constraint guarantees that U will have a public default constructor, which you can invoke.

like image 151
Sefe Avatar answered Oct 31 '22 18:10

Sefe