Reputation: 6284
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.
Upvotes: 13
Views: 1143
Reputation: 14007
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.
Upvotes: 11
Reputation: 9498
It surprisingly compiles if you switch from AnOperation
class to IAnOperation
interface:
class MyClass<T, U>
where T : Something
where U : IAnOperation<Something>
{
public U GetAnOperationOfSomething()
{
IAnOperation<T> anOperation = GenAnOperation();
return (U)anOperation;
}
private IAnOperation<T> GenAnOperation()
{
throw new NotImplementedException();
}
}
public class Something
{ }
public interface IAnOperation<T>
where T : Something
{ }
Upvotes: 1
Reputation: 11101
It's not clear what you are trying to do, you may want to expand the question. For instance, will there be several types of operation? Depending on that there are several possible solutions
If there are no operation subtypes, the U parameter is unnecessary. Just return AnOperation<T>
as that can already describe all operations.
// With only one type of operation
namespace ConsoleApp2
{
class MyClass<T> where T : Something
{
public AnOperation<T> GetAnOperationOfSomething()
{
AnOperation<T> anOperation = new AnOperation<T>();
return anOperation;
}
}
public class Something
{
}
public sealed class AnOperation<T>
where T : Something
{
}
}
If there are many kinds of operations (which seems likely), then the problem is that your class can't know about how to make them when it doesn't know about them. You need to give your class a function that can produce an U
instance to return.
// With a factory for operations
namespace ConsoleApp1
{
class MyClass<T, U>
where T : Something
where U : AnOperation<Something>
{
private readonly Func<T, U> operationMaker;
public MyClass(Func<T, U> operationMaker)
{
this.operationMaker = operationMaker;
}
public U GetAnOperationOfSomething(T something)
{
U anOperation = operationMaker(something);
return anOperation;
}
}
public class Something
{
}
public class AnOperation<T>
where T : Something
{
}
}
Alternatively you can constrain U
to new new()
so that your class can create them without further knowledge.
// With a "new()" contraint on U
namespace ConsoleApp3
{
class MyClass<T, U>
where T : Something
where U : AnOperation<Something>, new()
{
public U GetAnOperationOfSomething()
{
U anOperation = new U();
return anOperation;
}
}
public class Something
{
}
public class AnOperation<T>
where T : Something
{
}
}
Upvotes: 1
Reputation: 5141
Just to support @Evk's answer. See this example.
class Program
{
static void Main(string[] args)
{
MyClass<SomeSomething, AnOperation<Something>> foo =
new MyClass<SomeSomething, AnOperation<Something>>();
// AnOperation<SomeSomething> will cause a compile error.
var bar = foo.GetAnOperationOfSomething();
Console.WriteLine(bar != null);
Console.Read();
}
}
class MyClass<T, U>
where T : Something
where U : AnOperation<Something>
{
public U GetAnOperationOfSomething()
{
U anOperation = Activator.CreateInstance<U>();
return anOperation;
}
}
public class Something
{
}
public class AnOperation<T>
where T : Something
{
}
public class SomeSomething : Something
{
}
Upvotes: 1
Reputation: 101483
T
in your class is any class inheriting from Something
.
U
is any class inheriting from AnOperation<Something>
. Suppose you have child class like this:
public class ChildOperation<T> : AnOperation<T> {}
Now your U
might be ChildOperation<Something>
, and you cannot return instance of parent class (AnOperation<T>
) as instance of child class (U
which is ChildOperation<Something>
). It might also go wrong when T
is really ChildSomething
, because AnOperation<Something>
cannot be implicitly converted to AnOperation<ChildSomething>
. Long story short - AnOperation<T>
indeed cannot be always converted to your U
type, so compiler is correct.
Upvotes: 5
Reputation: 1937
Well the following works for me (EDIT: I am in framework v4.5.2):
class Program
{
static void Main(string[] args)
{
MyClass<Something, AnOperation<Something>> obj = new MyClass<Something, AnOperation<Something>>();
}
}
class MyClass<T, U>
where T : Something
where U : AnOperation<Something>
{
public U GetAnOperationOfSomething()
{
AnOperation<T> anOperation = new AnOperation<T>();
return anOperation as U;
}
}
public class Something
{
}
public class AnOperation<T>
where T : Something
{
}
Upvotes: 0