I'm coming from C++ template programming and get very confused with generics sometimes. As there is no method specialization, I tried using casting. Here's what I have:
public interface INonGen
{
void Get<T>(ref T value);
}
public interface IGen<U> : INonGen
{
}
public class Gen<U> : IGen<U>
{
private U u;
public void Get<T>(ref T value)
{
if (value is U)
{
value = (T) u;
}
else
throw new Exception();
}
}
This doesn't compile.
Is there I way I can make this cast?
The reason why I want this: With C++ templates, I would have made specializations for the types supported, and a non-specialied version that throws an exception.
The basic idea is this: A non-generic interface that has a generic method. Attempts to get the value using the correct type should work, attempts to use the wrong type can throw.
We should keep type safety, so I need to return an instance/value of the correct type. Any shortcuts over object are not acceptable, and neither is constraining the type in the non-generic interface.
The generic implementation is done to avoid duplication. I want to support multiple different types (but only a small set of types), but I want this to be decided when I instantiate a class (and define what T means); I still want the non-generic interface to allow access using any T; that is, I don't want the set of types explicitly in the interface.
When you cast an object to another, if the compiler can't find a conversion, it reports an error. Since the two type parameters are unconstrained, the only option is to use the as operator, which, instead of throwing an InvalidCastException, returns null when the cast fails. To use as you also need to constrain your generic type to classes.
public class Gen<U> : IGen<U>
{
private U u;
public void Get<T>(ref T value)
where T : class
{
if (value is U)
{
value = u as T;
}
else
throw new Exception();
}
}
If you don't want to add the constraint, you can cast to Object:
value = (T)(object)u;
There's a logical error in your code though. If value is U, what guarantees that u is T? For example:
var gen = new Gen<Base>();
gen.Set(new DerivedA()); // sets u;
var b = new DerivedB();
gen.Get(ref b);
In this case value is Base but not u is DerivedB. The cast will fail at runtime.
Update
After reading some of your comments, here's how I would've designed this:
public interface INonGen
{
object Value { get; }
}
public interface IGen<U> : INonGen
{
}
public class Gen<U> : IGen<U>
{
private U u;
public object Value
{
get { return u; }
}
}
And when pulling items out of the dictionary:
double value = (double)dictionary[key].Value;
An InvalidCastException will be thrown if there's no runtime conversion. Simple, no?
I'm not sure what the purpose of INonGen is in this context, especially as it has a generic method. If you get rid of that you can do this. Which compiles - I checked ;o)
public interface IGen<T>
{
void Get(ref T value);
}
public class Gen<T, U> : IGen<T> where U : T
{
private U u;
public void Get(ref T value)
{
if (value is U)
{
value = (T)u;
}
else
throw new Exception();
}
}
The point is that you cannot have the generic type argument on the interface method only, because that prevents you from specifying the U : T constraint in the implementing class. It has to be on the interface definition itself. And the implementing class has to know explicitly about both generic type arguments U and T, so that the compiler can verify the cast operation.
You could go down the route of using as but this is not type safe, so you have to handle cases where the result is null, rather than relying on the compiler to do it. Not recommended.
If in your real world example, INonGen has other non-generic methods, or generic methods where implementing classes do not need to know about the method's generic type arguments outside of the implementing method then you could reinstate it without any problems.
public interface INonGen
{
void NonGenericMethod();
void GenericMethod<V>(V parameter);
}
public interface IGen<T> : INonGen
{
void Get(ref T value);
}
public class Gen<T, U> : IGen<T> where U : T
{
private U u;
public void Get(ref T value)
{
if (value is U)
{
value = (T)u;
}
else
throw new Exception();
}
public void NonGenericMethod()
{
}
public void GenericMethod<V>(V parameter)
{
}
}
You need to use a concept call constraints ...
public class Gen<U> : IGen<U> where U : T
{
private U u;
public void Get<T>(ref T value)
{
if (value is U)
{
value = (T) u;
}
else
throw new Exception();
}
}
This tells you that U must be or derive from the argument supplied for T.
First, don't use ref for this either. Then your problem becomes moot.
public interface IThing
{
object Value { get; }
}
public interface IThing<T> : IThing
{
T Value { get; }
}
public class Thing<T> : IThing<T>
{
private T t;
public object Value
{
get
{
return this.Get();
}
}
public T Value<T>()
{
get
{
return this.t;
}
}
}
If you really want to accept some generic other, that is constrained to the right type
public interface ICrazyThing<T>
{
void Get<T>(ref T crazy);
}
public class CrazyThing<U, T> : IThing<T> where T : U
{
private U u;
public void Get<T>(ref T crazy)
{
crazy = this.u;
}
}
Even in crazy world, out would be a better choice than ref, since the value passed in is a pointless instantiation that has no bearing on the result.
Related
I'm trying to understand generic types. However, I think I'm getting confused with fakes.
On my system, I'd like to load something. It doesn't matter what it is, and it could be anything. The caller will konw what it is.
My approach is to use an interface, where the interface is simply
public interface ILoad
{
void Load<T>(T t);
}
In my Bll class, I have a method which is
public void Start(ILoad load)
{
load.Load<Survey>(this); // I'm telling it the type and passing it the object
}
public class FakeLoadForSurvey : ILoad //this class knows about Survey
{
public void Load<T>(T t)
{
t = new Survey(); //fails
{
}
It fails with the following error message
Cannot implicity convert type Survey to T
I hope the above example is clear enough.
How do I tell C# that T is of type Survey (or any other type)?
public class FakeLoadForSurvey : ILoad //this class knows about Survey
If the class implementing the interface knows the type for T, then move T to the interface:
public interface ILoad<T>
{
void Load(T t);
}
public class FakeLoadForSurvey : ILoad<Survey>
{
public void Load(Survey t)
{
t = new Survey();
}
}
Do note however that this won't affect the argument passed to Load().
So if some code calls it like this:
var surveyLoader = new FakeLoadForSurvey();
Survey surveyToLoad = null;
surveyLoader.Load(surveyLoader);
Then surveyToLoad is still null after Load() returns. If you don't want that, pass it with ref.
This code looks bad from a design point of view, since you are mixing generics with statically defined types. If you use generics, you should go all the way:
public interface ILoad
{
void Load<T>(T t) where T : new();
}
public class FakeLoadForSurvey : ILoad
{
public void Load<T>(T t) where T : new()
{
t = new T();
}
}
I am not sure what your intention is with the parameter you define, but it loses scope after the method, so t will never of any use outside of the Load<T> method.
public void Load<T>(T t) where T: Survey, new()
{
t = (T) new Survey(); // should succeed
}
But with a void return and without ref on the parameter this function still won't do anything useful.
I have code like this:
class Base { }
class Derived : Base { }
class Wrapper<T> {
public T Value { get; }
public Wrapper (T value) { Value = value; }
}
I would like to use Wrapper like this:
Wrapper<Base> wrapper = new Wrapper<Derived> (new Derived ());
But it ends up with this error:
Error CS0029 Cannot implicitly convert type 'Wrapper<Derived>' to 'Wrapper<Base>'
I tried creating method in Wrapper class that would act as converter
public Wrapper<TResult> To<TResult> () /* Constraints needed here. */ =>
new Wrapper<TResult> (Value);
But I'm missing some valid constraints. Current code ends up with error:
S1503 Argument 1: cannot convert from 'T' to 'TResult'
I would imagine constraints on To method could look like where T : TResult, but that's not valid constraints.
Any ways to implement converter from Wrapper<Derived> to Wrapper<Base> easily?
You could use covariance like so:
class Base { }
class Derived : Base { }
interface IWrapper<out T>
{
T Value { get; }
}
class Wrapper<T> : IWrapper<T>
{
public T Value { get; private set; }
public Wrapper(T value) { Value = value; }
}
class Program
{
static void Main(string[] args)
{
IWrapper<Base> wrapper = new Wrapper<Derived>(new Derived());
}
}
At first I would add a constraint to the class demanding that T must be of type Base:
class Base { }
class Derived : Base { }
class Wrapper<T> where T : Base // T must be (derived from) Base
{
public T Value { get; }
public Wrapper (T value) { Value = value; }
}
Secondly, a generic converter would be dangerous. What if someone tries to convert a Wrapper<Gnu> to a Wrapper<Lion>?
So I'd take a step back and make a non-generic converter that simply converts to Wrapper<Base>:
public Wrapper<Base> ToBase()
{
return new Wrapper<Base>(Value);
}
And this works because of the constraint for T at class level.
C# is actually a language known for a high level of type safety. But you can get around it and do what you asked for in the comment by ommitting any constraints and just trying to cast whatever comes in:
public Wrapper<TResult> To<TResult>() where TResult : class
{
return new Wrapper<TResult>(Value as TResult);
}
You need the class constraint and the as operator because a direct cast between two generic parameters is not compilable (as the IL depends too much on the specific types).
But this will return Wrapper instances with Value set to null if the types don't match. And it will work with derived types instead of base types too. So take care. You may add some extra checks for that. And take care of the gnus :)
UPDATE:
A safer way:
public Wrapper<TResult> To<TResult>() where TResult : class// TResult must also be (derived from) Base
{
if (!typeof(TResult).IsAssignableFrom(typeof(T)))
throw new InvalidCastException();
return new Wrapper<TResult>(Value as TResult);
}
This checks that T is derived from TResult and throws an InvalidCastException if not. You may refine that for your needs.
The problem you're encountering is that the generic types Wrapper<Base> and Wrapper<Derived> are two completely different classes for the .NET Framework
What you could do is creating a new Wrapper of type Base:
Wrapper<Base> wrapper = new Wrapper<Base>(new Derived());
Or to complete your To-method approach:
public Wrapper<TResult> To<TResult>() where TResult : T
=> new Wrapper<TResult>( (TResult)Value ); // This could throw an error
public bool TryCastTo<TResult>(out Wrapper<TResult> derivedWrapper) where TResult : T
{
derivedWrapper = null;
// EDIT: changed to the version from René Vogt since this is much cleaner and mine had a little error
if (!typeof(T).IsAssignableFrom(typeof(TResult)))
{
return false;
}
derivedWrapper = new Wrapper<TResult>( (TResult)Value );
return true;
}
The usage would be:
Wrapper<Derived> derivedWrapper1 = wrapper.To<Derived>();
Wrapper<Derived> derivedWrapper2;
bool success = wrapper.TryCastTo<Derived>(out derivedWrapper2);
ReSharper suggests me to make type parameter T contravariant by changing this:
interface IBusinessValidator<T> where T: IEntity
{
void Validate(T entity);
}
Into this:
interface IBusinessValidator<in T> where T: IEntity
{
void Validate(T entity);
}
So what is different between <T> and <in T>? And what is the purpose of contravariant here?
Let say I have IEntity, Entity, User and Account entities. Assuming that both User and Account have Name property that need to be validated.
How can I apply the usage of contravariant in this example?
So what is different between <T> and <in T>?
The difference is that in T allows you to pass a more generic (less derived) type than what was specified.
And what is the purpose of contravariant here?
ReSharper suggests to use contravariance here because it sees the you're passing the T parameter into the Validate method and wants to enable you to broaden the input type by making it less generic.
In general, contravariance is explained to length in Contravariance explained and in Covariance and contravariance real world example, and of course throughout the documentation on MSDN (there is a great FAQ by the C# team).
There is a nice example via MSDN:
abstract class Shape
{
public virtual double Area { get { return 0; }}
}
class Circle : Shape
{
private double r;
public Circle(double radius) { r = radius; }
public double Radius { get { return r; }}
public override double Area { get { return Math.PI * r * r; }}
}
class ShapeAreaComparer : System.Collections.Generic.IComparer<Shape>
{
int IComparer<Shape>.Compare(Shape a, Shape b)
{
if (a == null) return b == null ? 0 : -1;
return b == null ? 1 : a.Area.CompareTo(b.Area);
}
}
class Program
{
static void Main()
{
// You can pass ShapeAreaComparer, which implements IComparer<Shape>,
// even though the constructor for SortedSet<Circle> expects
// IComparer<Circle>, because type parameter T of IComparer<T> is
// contravariant.
SortedSet<Circle> circlesByArea =
new SortedSet<Circle>(new ShapeAreaComparer())
{ new Circle(7.2), new Circle(100), null, new Circle(.01) };
foreach (Circle c in circlesByArea)
{
Console.WriteLine(c == null ? "null" : "Circle with area " + c.Area);
}
}
}
How can I apply the usage of contravariant in this example?
Let's say we have our entities:
public class Entity : IEntity
{
public string Name { get; set; }
}
public class User : Entity
{
public string Password { get; set; }
}
We also have a IBusinessManager interface and a BusinessManager implementation, which accepts an IBusinessValidator:
public interface IBusinessManager<T>
{
void ManagerStuff(T entityToManage);
}
public class BusinessManager<T> : IBusinessManager<T> where T : IEntity
{
private readonly IBusinessValidator<T> validator;
public BusinessManager(IBusinessValidator<T> validator)
{
this.validator = validator;
}
public void ManagerStuff(T entityToManage)
{
// stuff.
}
}
Now, lets say we created a generic validator for any IEntity:
public class BusinessValidator<T> : IBusinessValidator<T> where T : IEntity
{
public void Validate(T entity)
{
if (string.IsNullOrWhiteSpace(entity.Name))
throw new ArgumentNullException(entity.Name);
}
}
And now, we want to pass BusinessManager<User> an IBusinessValidator<T>. Because it is contravariant, I can pass it BusinessValidator<Entity>.
If we remove the in keyword, we get the following error:
If we include it, this compiles fine.
To understand ReSharper's motivation, consider Marcelo Cantos's donkey gobbler:
// Contravariance
interface IGobbler<in T> {
void gobble(T t);
}
// Since a QuadrupedGobbler can gobble any four-footed
// creature, it is OK to treat it as a donkey gobbler.
IGobbler<Donkey> dg = new QuadrupedGobbler();
dg.gobble(MyDonkey());
If Marcelo had forgotten to use the in keyword in the declaration of his IGobbler interface, then C#'s type system wouldn't recognise his QuadrupedGobbler as a donkey gobbler, and so this assignment from the code above would fail to compile:
IGobbler<Donkey> dg = new QuadrupedGobbler();
Note that this wouldn't stop the QuadrupedGobbler from gobbling donkeys - for instance, the following code would work:
IGobbler<Quadruped> qg = new QuadrupedGobbler();
qg.gobble(MyDonkey());
However, you wouldn't be able to assign a QuadrupedGobbler to a variable of type IGobbler<Donkey> or pass it to some method's IGobbler<Donkey> parameter. This would be weird and inconsistent; if the QuadrupedGobbler can gobble donkeys, then doesn't that make it a kind of donkey gobbler? Luckily, ReSharper notices this inconsistency, and if you leave out the in in the IGobbler declaration, it will suggest that you add it - with the suggestion "Make type parameter T contravariant" - allowing a QuadrupedGobbler to be used as an IGobbler<Donkey>.
In general, the same logic outlined above applies in any case where an interface declaration contains a generic parameter that is only used as the type of method parameters, not return types.
I have a base abstract class which is meant to allow implementors to return different types:
protected abstract T GetMyValue<T>();
I would like the implementing class to be able to do something like this:
T myResult;
public override T GetMyValue<T>()
{
return _myResult;
}
I would like the caller to specify the type:
int i = obj.GetMyValue<int>();
Is this possible?
Like an object extension?
public static class ObjectExtension
{
public static T GetValue<T>(this object obj)
{
var converter = TypeDescriptor.GetConverter(typeof(T));
if (converter == null)
return default(T);
return (T)converter.ConvertFrom(obj);
}
}
One option would just be to store the field as an object. This will cause boxing for structs, but there isn't better way without a more significant change.
class Derived : Base
{
private object o;
protected override T GetMyValue<T>()
{
if (o is T)
return (T)o;
return default(T);
}
}
You can't really have a generic field unless the abstract class is itself generic, not the specific method. Of course this is a significant design change. One implementation can now only return one type. Something like
abstract class BaseClass<T>
{
protected abstract T GetMyValue();
}
class Derived : Base<int>
{
private int i;
protected override int GetMyValue()
{
return i;
}
}
You somehow have to make the field have the same type that you want to return. Here's a trick for that:
public override T GetMyValue<T>()
{
return ValueHolder<T>.GetMyValue();
}
class ValueHolder<T> {
static T myResult;
public static T GetMyValue()
{
return myResult;
}
}
Because this is a static class there can only be one such value.
If you want to have one value per type in your instance you need a Dictionary<Type, object>.
I have the following generic class
public class Home<T> where T : Class
{
public string GetClassType
{
get{ return T.ToString() }
}
}
Now, I'm getting an Object X which I know for sure is Home:
public void DoSomething(object x)
{
if(x is // Check if Home<>)
{
// I want to invoke GetClassType method of x
// but I don't know his generic type
x as Home<?> // What should I use here?
}
}
Can I even make a cast without specifying the generic type of the class?
If you're sure the argument to DoSomething will be a Home<T>, why not make it a generic method?
public void DoSomething<T>(Home<T> home)
{
...
}
Of course, it would be even easier if DoSomething should logically be an instance method on Home<T>.
If you really want to stick with what you have, you could use reflection (untested):
public void DoSomething(object x)
{
// null checks here.
Type t = x.GetType();
if (t.IsGenericType &&
&& t.GetGenericTypeDefinition() == typeof(Home<>))
{
string result = (string) t.GetProperty("GetClassType")
.GetValue(x, null);
Console.WriteLine(result);
}
else
{
... // do nothing / throw etc.
}
}
What if Home derived from a base class?
public class Home
{
public virtual string GetClassType { get; }
}
public class Home<T> : Home
where T : class
{
public override string GetClassType
{
get{ return T.ToString() }
}
...
}
and then
public void DoSomething(object x)
{
if(x is Home)
{
string ct = ((Home)x).GetClassType;
...
}
}
How about making the function generic?
public void DoSomething<T>(object x)
{
if(x is Home<T>)
{
x as Home<T> ...
}
}
Edit:
Another possiblity would be to create an interface which holds the property GetClassName so you would only need to check if it is of that interface.
public interface IDescribeClass
{
public string GetClassName { get; set; }
}
BTW: I would use the full qualified name
public string ClassType
{
get{ return typeof(T).FullName; }
}
Have you tried changing your method definition to something like this?
public void DoSomething<T>(Home<T> x)
{
}
I know this is an old thread but all answers posted so far have not directly addressed this question and instead only suggested workarounds (i.e. "use reflection", "make your DoSomething() method generic" or "create a non-generic base class and call this base class' method").
Can I even make a cast without specifying the generic type of the class?
So to clearly answer your question: No it is not possible. Due to the covariance constraints in C# you cannot cast into a generic class.
In more detail: I am assuming you would want to use x as Home<object> as the lowest common denomitator in order to be be able to call toString() provided by the Object class. Casting your object x to Home<object> would require covariance which is not possible with classes (only generic interfaces and delegates can be covariant). while this is great to prevent mistakes at compile time, it is certainly an annoyance when wanting to access methods on generic classes, as in your case. #n8wrl answer is probably your best shot in terms of "casting".
That being said, you could also go for an interface-based solution, using the out flag on your T parameter:
interface IHome<out T> {
string GetClassType { get; }
}
public class Home<T> : IHome<T> where T : class
{
public string GetClassType
{
get { return typeof(T).Name; }
}
}
Then this should work:
public void DoSomething(object x)
{
if(x is // Check if Home<>)
{
var y = x as IHome<object>;
var z = y.GetClassType;
}
}