Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# method generic return type casting

Tags:

c#

generics

I am trying to create a method in an interface with a generic return type but I fail to cast the generic to a specific type/class. But if I put the generic on the interface instead of the method I am able to do the casting.

In other words, why does this work

public class Rain {
    public string propA {get;set;}
}
public interface IFoo<T> {
    T foo();
}

public class Bar : IFoo<Rain> {
    Rain foo() { 
        //...
        return new Rain();
    }
}

public bar = new Bar();
Rain rain = bar.foo();

But it is not possible to do this?

public class Rain {
    public string propA {get;set;}
}
public interface IFoo {
    T foo<T>();
}

public class Bar : IFoo {
    T foo<T>() { 
        //...
        return new Rain();
    }
}

public bar = new Bar();
Rain rain = bar.foo<Rain>();

Is there any other way around ( without using Convert.ChangeType())?

like image 301
Develope Cruz Avatar asked Nov 12 '18 09:11

Develope Cruz


2 Answers

The second code snippet is impossible to compile because Rain is not T.

When the type parameter is supplied in the class, there's no problem because the method can only return the type that was already supplied in the class declaration. In other words - T foo() becomes Rain foo.

However, when the type parameter is supplied to the method, then the method is obligated to return whatever type is supplied to it - so you can't return anything other than T. In other words, the compiler can't enforce the calling method to only use foo<Rain>(), but it can enforce the foo method to return T.

like image 64
Zohar Peled Avatar answered Oct 22 '22 19:10

Zohar Peled


The difference is:

// On this line you specify that the interface's generic type paramter 'T' is of type 'Rain',
// so the method implements the interface and returns a 'Rain'
public class Bar : IFoo<Rain> {
    Rain foo() { // <= implements IFoo<Rain>.foo, where T = Rain so foo returns 'Rain'
        return new Rain();

// In this version, the generic type parameter is declared on the method. It could be any type when the method is called, yet you always return a 'Rain'
public class Bar : IFoo {
    T foo<T>() { // <= implements IFoo.foo<T> but the type of T is not specified yet
        return new Rain();

The "solution" for this depends on what your intentions are.

  • Why would you not want the generic parameter on the interface?
  • Also, why would you want a generic parameter on the foo method, if you always return Rain anyways?

Of course, in any case, you could just cast it like this:

T Foo<T>()
{
    object result;
    result = new Rain();
    return (T)result; // note, this will throw at runtime if 'result' cannot be cast to 'T'
}

// call like this:
Bar.Foo<Rain>();

But I think your first approach IFoo<T> makes perfect sense so why not use it?

UPDATE

Based on your comment: You can also define multiple generic parameters:

public interface IFoo<T1, T2>
{
    T1 foo();
    T2 foo2();
}

// implementation:
public class Bar : IFoo<Rain, Other>
{
    Rain foo() { /* ... */ }
    Other foo2() { /* ... */ }
}
like image 34
marsze Avatar answered Oct 22 '22 20:10

marsze