Reputation: 3290
I have the following code
public interface IInterface
{
}
public class GenericClass<TSomeClass>
where TSomeClass : class
{
public TSomeClass SomeMethod(TSomeClass someClass = null)
{
return SomeClass.SomeClassStaticInstance; //ERROR:Cannot implicitly convert type 'SomeClass' to 'TSomeClass'
return (TSomeClass)SomeClass.SomeClassStaticInstance; //ERROR:Cannot convert type 'SomeClass' to 'TSomeClass'
return SomeClass.SomeClassStaticInstance as TSomeClass; //Works when "where TSomeClass : class" clause added
}
}
public class SomeClass : IInterface
{
public static SomeClass SomeClassStaticInstance = new SomeClass();
}
It generates the compile time errors noted in the comments on the appropriate lines.
I would like to know why I can't just use the first line that generates an error? SomeClass
implements IInterface
but I have to mess around with the as
keyword.
I've tried changing GenericClass<TSomeClass>
to GenericClass<out TSomeClass>
but then I get another compile time error Only interface and delegate type parameters can be specified as variant.
which persists, even if i remove the where TSomeClass : class
clause.
What am I missing ... it obviously works because I can 'force it to' using the where TSomeClass : class
clause and the return SomeClass.SomeClassStaticInstance as TSomeClass;
statement!
I do actually need the where TSomeClass : class
otherwise I get yet another compile time error with TSomeClass someClass = null
... A value of type '<null>' cannot be used as a default parameter because there are no standard conversions to type 'TSomeClass'
.
So it's basically compile time errors all the way down! Thanks.
Upvotes: 0
Views: 240
Reputation: 20353
At the moment your only constraint is that TSomeClass
is a class, so if you were to instantiate GenericClass
with a type that isn't SomeClass
, or a type that derives from it, return SomeClass.SomeClassStaticInstance;
clearly isn't going to work.
For example:
var genericClass = new GenericClass<SomeRandomClass>(); // fine
var randomClass = genericClass.SomeMethod(new SomeRandomClass()); // not fine, a SomeClass is returned!
I'm not sure where IInterface
currently fits into all this?
Maybe you want SomeMethod to return an object that implements IInterface
; if so, you need to further constrain your generic type, and have your method return an IInterface
:
public class GenericClass<TSomeClass> where TSomeClass : class, IInterface
{
public IInterface SomeMethod(TSomeClass someClass = null)
{
return SomeClass.SomeClassStaticInstance; //This now compiles!
}
}
As for why return SomeClass.SomeClassStaticInstance as TSomeClass;
worked originally, this was merely a coincidence.
as
is a safe cast operator, which returns null
if the attempted cast fails. As you constrained TSomeClass
to be a class, a null
return value would be valid. You would always return null
however, if the generic type you specified wasn't SomeClass
.
Upvotes: 1
Reputation: 33857
I believe that this would work:
public interface IInterface
{
}
public class GenericClass<TSomeClass>
where TSomeClass : IInterface
{
//Not sure what the input parameter is for?
public IInterface SomeMethod(TSomeClass someClass = null)
{
return SomeClass.SomeClassStaticInstance;
}
}
public class SomeClass : IInterface
{
public static IInterface SomeClassStaticInstance = new SomeClass();
}
However as noted by others, the question here is really what generics are being used for in this instance - is there possibly a better choice of how to structure your code?
Very hard to tell what the purpose of this would be from the structure you have given us.
Upvotes: 1
Reputation: 14677
I would like to know why I can't just use the first line that generates an error? SomeClass implements IInterface but I have to mess around with the as keyword.
You have to use (TSomeClass)YourInstance
cast as language is designed to prevent any assumptions on casting generic arguments. Read more.
Because the implicit conversion on type of argument TSomeClass can not be predicted unless you put a constraint of less inherited type e.g. interface. You should change your constraint to where TSomeClass : IInterface
for variance.
Using as
key worked because it will return null if the type is incompatible, which is why you need class
constraint. After changing your constraint to interface the as
statement will become invalid.
In your factory method you should return IInterface
so the generic type argument can accept a direct cast.
Upvotes: 1