tags:

views:

136

answers:

5

Could I have something like this:

int x = MyMethod<int>();
string y = MyMethod<string>();

So, one method returning different types based on T. Of course, there would be logic inside the method to ensure it was returning the correct thing.

I can never get something like this to run. It complains that it can't cast the return value to T:

public static T MyMethod<T>()
{
  if(typeof(T) == typeof(Int32))
  {
    return 0;
  }
  else
  {
    return "nothing";
  }
}
+10  A: 

Try the following

public static T MyMethod<T>() {
  if ( typeof(T) == typeof(Int32) ) {
    return (T)(object)0;
  } else {
    return (T)(object)"nothing";
  }
}

The trick here is the casting to object. What you are trying to do is inherently unsafe since the compiler cannot infer that 0 or "nothing" are convertible to any given T. It is unbounded after all. So just tell the compiler explicitly it's not safe with casting to object.

JaredPar
This works. So, the only think you were doing differently is casting it to an object first, then to T?
Deane
While this solves the compile time error, but it still doesn't make sense. If T is, say, a page type, it doesn't make sense to return a string cast to T via this (object) hack. The only reason this compiles is because both strings and integers can be cast to object, and objects can be case to their descendant types (ie, everything).
David Lively
A: 

You could do, if you want a "null" type value returned if it's not an int. But if you want a string in all other cases, check the other answers.

public static T MyMethod<T>()
{
  if(typeof(T) == typeof(Int32))
  {
    return (T)(object)0;
  }
  else
  {
   return default(T); // or null
  }
}
Mikael Svenson
For the string sample the OP wants it to return a specific string value, not default(T). You are missing a return type as well.
JaredPar
True.. and I'm missing it because I copied the code and missed that part before the edit.
Mikael Svenson
This won't work - there's no conversion between int and T
Lee
@Lee - it will. try it.
dkackman
@dkackman - It will work now the answer has been edited...
Lee
@Lee - ah. didn't see the pre-edit version...
dkackman
A: 

@Nick is correct in his comment that your code won't compile. But assuming you mean:

public static T MyMethod<T>()
{
  if(typeof(T) == typeof(Int32))
  {
    return 0;
  }
  else
  {
    return "nothing";
  }
}

It is can be tricky to make generics non generic like you are attempting (and really contravenes the whole point), but this should work:

public static T MyMethod<T>()
{
  if(typeof(T) == typeof(Int32))
  {
    return (T)(object)0;
  }
  else if(typeof(T) == typeof(string))
  {
    return (T)(object)"nothing";
  }

   return default(T);
}
dkackman
Yeah, that was the same solution as above. I'm assuming that I just had to cast to an object first, since there's no direct cast from int to T. But there's a cast from int to object, and object to T. Is that correct?
Deane
Because you check for typeof(T) yes. Really the else clause should check for typeof(string) otherwise somethihng like MyMethod<double> would fail. I'll edit the example.
dkackman
A: 

This isn't valid as there is no guarantee that T is castable/convertable to string or int.

Generics are NOT variants - ie, types that can hold anything - they are compile-time resolved to a real type. Your code doesn't compile because it shouldn't. You can get around the compile-time issues using some of the hacks posted here, but this is really a logic problem. Step back and rethink what you're trying to do instead of trying to get around the compiler.

David Lively
Can I somehow constrain T to be only string or int?
Deane
@Deane - you can't
dkackman
@Deane - In VB or similar scripted languages, you can return whatever you want from a function - int, string, float, page, webservice reference, what have you. In strongly-typed languages, that's not the case - because the compiler expects you to take control of the reigns and not let it do your work for you. What does the CALLING CODE look like that you need this kind of result?!
David Lively
In different places in my code, I need either a string or an int back from this method. The way I have it now, it always returns a string, and I do the conversion to an int in the calling code. But that means, everywhere I call it, I have to do this conversion and the accompanying error checking. I was thinking if I could do the conversion in the method itself (by getting it to return an int when I needed it to), then I could keep all the conversion and error checking code in one place. Does that make sense?
Deane
Then you need different methods. Unlike weakly-typed languages such as VB and PHP, strongly-typed languages like C and C# require that each method has a fixed return type. This is a safety measure and provides all sorts of benefits, such as compile-time type checking.You can trick the compiler, but you shouldn't. Rethink your logic and determine a better solution.
David Lively
+1  A: 

Slightly off topic, but if you're trying to do this thing then it may be that your design is wrong...

Why not overload the method and set the result as an out paramter:

void MyMethod(out int result)
{
  result=0;
}

void MyMethod(out string result)
{
  result="my value";
}

Then you can say:

int value;
MyMethod(out value);

And the compiler will select the right version

Sean
Is it not a correct use of generics to be able to use the same logic for different types? I just have a method that has a bunch of logic, but in different situations, I need different types back from it. Is this not the right way to use generics?
Deane
But you're switching on type, which is going to cause a problem... I'll post an alternative
Sean
Yeah, that would work. Just, less graceful it seems, because I now have three methods -- one for each type, and one for the core logic. Why is switching on type an issue?
Deane
+10 billion for seeing the real problem.
David Lively
And, Deane, NO, that's not the point of generics. The same logic in your case is that you want to return 0 if it's an int, "nothing" if it's a string, I guess http://www.google.com if it's a page, probably c:\ if it's a Path instance... the problem is that you're expecting the compiler to do the work for you. Overloaded methods are a good way to go.**OR, just return NULL** regardless of the type.
David Lively