Why does the following compile?
public IList<T> Deserialize<T>(string xml)
{
if (typeof(T) == typeof(bool))
return (IList<T>)DeserializeBools(xml);
return null;
}
private static IList<bool> DeserializeBool(string xml) { ... do stuff ... }
But this doesn't
public MyClass<T> GetFromDb<T>(string id)
{
if (typeof(T) == typeof(bool))
return (MyClass<T>)GetBoolValue(id); <-- compiler error here
return null;
}
private static MyClass<bool> GetBoolValue(string id) { ... do stuff ... }
The reason interfaces work is that any object might implement IList<T> (unless it's known to be an instance of a sealed type which doesn't implement it, I guess) - so there's always a possible reference type conversion to the interface.
In the latter case, the compiler isn't willing to do that because it doesn't really know that T is bool, despite the previous if statement, so it doesn't know what conversion to try between MyClass<T> and MyClass<bool>. The valid conversions to generic types are pretty limited, unfortunately.
You can fix it fairly easily:
return (MyClass<T>)(object) GetBoolValue(id);
It's ugly, but it should work... and at least in this case it won't be causing any boxing.
C# 4.0 allows declaration of covariance and contravariance on parameterized interface and delegate types.
What happens if you replace
return (MyClass<T>)
with
return (MyClass<bool>)
Related
This question already has answers here:
Cast from Generics<T> to Specific SubClass
(9 answers)
Closed 7 years ago.
I have a simple interface and two classes implement it:
public interface IMovable { }
public class Human : IMovable { }
public class Animal : IMovable { }
The following generic method results in a compile-time error: Cannot convert type 'Human' to 'T'
public static T DoSomething<T>(string typeCode) where T : class, IMovable
{
if (typeCode == "HUM")
{
return (T)new Human(); // Explicit cast
}
else if (typeCode == "ANI")
{
return (T)new Animal(); // Explicit cast
}
else
{
return null;
}
}
But when the as keyword is used, all is fine:
public static T DoSomething<T>(string typeCode) where T : class, IMovable
{
if (typeCode == "HUM")
{
return new Human() as T; // 'as'
}
else if (typeCode == "ANI")
{
return new Animal() as T; // 'as'
}
else
{
return null;
}
}
Why does as work but explicit cast doesn't?
Short answer is, because T doesn't have to be of the correct type. Compiler is really trying to help you here, because you are doing something which might easily fail in runtime.
E.g. consider what happens with:
var result = DoSomething<Human>("ANI");
Longer answer is, you shouldn't be casting at all. Casting indicates problems with your OOP design, and is especially wrong when using generics: you lose the whole point of generics, actually. Generics are supposed to allow you create a "template" which abstracts away the actual type, leaving you to worry about the algorithm itself instead of concrete types.
In this case, you probably don't need generics at all. Your method is basically a less safer way of doing this:
public static T DoSomething<T>() where T : new()
{
return new T();
}
or this:
public static IMovable DoSomething(string typeCode)
{
if (typeCode == "HUM")
return new Human();
if (typeCode == "ANI")
return new Animal();
return null;
}
To silence the compiler, you may also add an intermediate cast, which tells the compiler you went an extra step to indicate that you really want to cast it this way: For example, using
(T)(object)new Human()
or
(T)(IMovable)new Human()
will both pass compilation, although the conversion from IMovable to T is no safer than the original code, and casting an object to T even unsafer. But this is not the solution to your underlying issue, which is design related.
With your code, it is perfectly possible to call DoSomething<Animal>, and then you have (Animal)new Human().
That is biologically correct, but your model does not allow it.
Do you really need generics here? Maybe you just want to return IMovable in this case.
Under the covers, the 'as' will do an 'is' check first before attempting the cast. So it will not attempt it if it can't cast it and will then return null.
I have a piece of code that works like this:
public IEnumerable<ICacheMember> Flubvert( IEnumerable<ICacheMember> members )
{
// do some stuff to members
return members;
}
However I am confused as to why I can't do this:
public IEnumerable<T> ExecuteFlubversion<T>( IEnumerable<T> memberList ) where T: class,ICacheMember
{
return Flubvert( memberList );
}
Surely the constraint on the generic should guarantee that memberListis an IEnumerable of the ICacheMembertype? Do I really need to convert a collection of existing ( but implicit ) ICacheMember objects into explicit ICacheMember objects and then convert them back afterwards? I can understand that I might need to convert them back given the method signature of Flubvert but I don't see why I should have to convert them in the method call. This is what I am doing in the working code but it seems completely out of keeping with the generally elegant behaviour of generics so I think I must be misunderstanding something about how this is supposed to operate.
First of all covariance of IEnumerable<out T> (and other generic types) only works when T is a reference type, so you need:
public IEnumerable<ICacheMember> ExecuteFlubversion<T>(IEnumerable<T> memberList)
where T: class, ICacheMember // NOTE 'class'
{
var flub = Flubvert(memberList); // can you call with 'memberList'?
return flub; // can you return that type?
// depending on what 'Flubvert' does, maybe return 'IEnumerable<T>'
// and say:
// return (IEnumerable<T>)flub;
}
Also note that I changed the return value. The C# compiler cannot guarantee that the returned object from the non-generic Flubvert method is anything more specific than IEnumerable<ICacheMember>.
Lets say you have:
interface ICacheMemberSub : ICacheMember
{
...
}
And you call your function like this:
ExecuteFlubversion<ICacheMemberSub>(cacheMember);
This function will try to return an object with type IEnumerable<ICacheMember>, and that is not necessarily castable to IEnumerable<ICacheMemberSub>, hence the error.
At risk of not directly answering the question, can you change the signature of Flubvert to a generic? If you make Flubvert generic, the rest of the method code will stay the same and you can still assume that the members will be implementers of ICacheMember.
public IEnumerable<T> Flubvert<T>(IEnumerable<T> members)
where T : class, ICacheMember
{
// do some stuff to members
return members;
}
public IEnumerable<T> ExecuteFlubversion<T>(IEnumerable<T> memberList)
where T : class,ICacheMember
{
return Flubvert(memberList);
}
I'm trying to get a generic interface with implentation for handling my xml:
IXmlService
List<T> Load<T>() where T : class;
XmlService
public List<T> Load<T>() where T : class {
Type type = typeof(T);
if (type == typeof(TicketData)) { return XmlTicketService.LoadInternal(); } // Error: Unable to cast from List<TicketData> to List<T>
And the XmlTicketService.LoadInternal() knows the type and should return to Service
internal static List<TicketData> LoadInternal() {
List<TicketData> result = new List<TicketData>();
ThreadPool.QueueUserWorkItem(
delegate {
try {
XDocument data = XDocument.Load(_xmlPath);
var query = (from element in data.Root.Descendants("Ticket")
select new TicketData() {
Hope u have and advices for me :)
Well, in this case you can just cast, going via object:
if (typeof(T) == typeof(TicketData))
{
return (List<T>) (object) XmlTicketService.LoadInternal();
}
The object cast first basically forces the compiler to treat it as a "normal" cast.
... but personally I think that raises a design smell, where you should probably be creating a generic interface with a non-generic method, and implementing ILoadable<TicketData> or whatever. Basically your method isn't really generic - it has specific handling for specific types, which should always make you question whether your design is really appropriate.
How to write a generic method in Java.
In C# I would do this
public static T Resolve<T>()
{
return (T) new object();
}
Whats the equivalent in Java?
First, your C# example is wrong; it will throw an InvalidCastException unless typeof(T) == typeof(object). You can fix it by adding a constraint:
public static T Resolve<T>() where T : new() {
return new T();
}
Now, this would be the equivalent syntax in Java (or, at least, as close as we can get):
public static <T> T Resolve() {
return (T) new T();
}
Notice the double mention of T in the declaration: one is the T in <T> which parameterizes the method, and the second is the return type T.
Unfortunately, the above does not work in Java. Because of the way that Java generics are implemented runtime type information about T is not available and so the above gives a compile-time error. Now, you can work around this constraint like so:
public static <T> T Resolve(Class<T> c) {
return c.newInstance();
}
Note the need to pass in T.class. This is known as a runtime type token. It is the idiomatic way of handling this situation.
As other commenters have pointed out, you can do this with Java as well - with as much of a possibility to create a casting exception at runtime:
#SuppressWarnings("unchecked")
public static <T> T resolve() {
return (T) new Object();
}
Unless you use the #SuppressWarnings annotation, however, Java's type erasure comes into play and you will get a compiler warning. The exception will also occur somewhere else: whereever you are trying to use it:
String s = <String>resolve();
will throw the exception.
On the other hand, you probably wanted to use new T() in C# anyway. This you cannot do in Java. The suggested workaround is to use Class<T> as a type parameter if you need to rely on type information at runtime. For your example, this would mean that you have to refactor it to this version:
public static <T> T resolve(Class<T> type) {
try {
return type.newInstance();
} catch(Exception e) {
// deal with the exceptions that can happen if
// the type doesn't have a public default constructor
// (something you could write as where T : new() in C#)
}
}
By the way, you can use this also to get rid of the warning in the previous case and to place the runtime exception at a more sensible line:
public static <T> T resolve(Class<T> type) {
return type.cast(new Object());
}
This piece of code will behave exactly like the one you gave as an example - including an exception that occurs when T is any type different from Object.
Try this http://java.sun.com/docs/books/tutorial/extra/generics/methods.html
public static <T> T Resolve()
{
return (T) new Object();
}
Be careful about (T) but I am not sure that this is correct. I know that generic cast causes a lot of problems. I have already spent with it a lot of time...
You want some kind of factory:
public interface MyFactory<T> {
T newInstance();
}
Then that can be passed into where it is needed. In your code:
public static T resolve<T>(MyFactory<T> factory) {
return factory.newInstance();
}
Note: There is absolutely no reason to be using reflection for this!!
I'm trying to write a method like this:
public static T Test<T>()
{
if (typeof(T)==typeof(string))
return "1241";
// do something else
}
but I can't seem to figure out how to pull it off. I want to return values depending on the type of T that the method was invoked with. I need to return strings, int's, custom classes, List etc.
The actual usecase is some custom serialization code where it is essential that the deserializing code knows the type of the object it should produce.
Clarification:
the example above gives the following error:
Cannot convert string to type T
The ideal solution would work on value types and reference types, and would not include a dummy parameter for overload resolution.
I'm starting to doubt if that ideal solution exists though.
Thanks, Lucas
The intermediate cast to object isn't ideal, but something like this should do the trick:
public static T Test<T>()
{
if (typeof(T) == typeof(string))
return (T)(object)"1241";
// do something else
}
You have to cast the return value to T, e.g. something like this for reference types:
public static T Test<T>() where T : class
{
if (typeof(T)==typeof(string))
return "1241" as T;
return default(T);
}
Beware! The solution below does not work (verified using the Mono gmcs C# compiler).
However, it should work by my reading of the C# standard, since the overload resolution should favour the non-generic version of the method when possible. The relevant section in ECMA-334 is 25.1.7: “Overloading in generic classes”. Furthermore, Eric Lippert seems to say so, too, in a blog posting.
Feedback would be appreciated: why doesn't this work as expected?
You have got unrelated types and unrelated behaviour: this code screams “use overloading!”
Generics would be appropriate for unrelated types, yet identical (or highly similar) behaviour.
Do this (complete test program to reproduce behaviour):
using System;
class TestClass {
public static T Test<T>() {
return TestWith(default(T));
// do something else
}
public static string TestWith(string dummy) {
// Used only for `string`.
return "string";
}
public static T TestWith<T>(T dummy) {
// Used for everything else.
return dummy;
}
static void Main() {
Console.WriteLine("Expected \"0\", got \"{0}\"", Test<int>());
Console.WriteLine("Expected \"string\", got \"{0}\"", Test<string>());
}
}
Compiled with gmcs, this yields:
Expected "0", got "0"
Expected "string", got ""
Here, the parameter serves only for the disambiguation of the overloaded call. Explicit generic parameters cannot be used here since one of the functions (the string specialization) isn't generic.
Try
public static T Test<T>() where T : class
{
if (typeof(T) == typeof(string)) return "asdf" as T; // do something else
// do something else
}
Can you use ChangeType?
public static T Test<T>()
{
if (typeof(T)==typeof(string))
return (T)Convert.ChangeType("1234", typeof(T), CultureInfo.InvariantCulture);
return default(T);
}
I found the solution:
public static T Test<T>()
{
if (typeof(T) == typeof(string))
return (T)(object)"1241"; // this works.
// do something else
}
Thanks for all the answers.
public static T Test<T>()
{
if (typeof(T)==typeof(string))
return (T)Convert.ChangeType("1241", typeof(T));
return default(T);
}
I've not tested it though :-)