简体   繁体   中英

C# method generic return type casting

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() )?

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 .

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() { /* ... */ }
}

If you need your interface to support an arbitrary set of classes, and what classes are used is determined by the caller then something like this is the most general solution.

public class Bar : IFoo {
    T foo<T>() { 
        if (typeof(T)==typeof(Rain))
           return new Rain() as T;
        if (typeof(T)==typeof(Snow))
           return new Snow() as T;
        Throw new ArgumentException("Not implemented for " + typeof(T).Name);
    }
}

If all your T's have a common interface and that is what you are interested in you could do;

public class Snow : IWeather {...}
public class Rain: IWeather {...}
public class Bar : IFoo {
    IWeather  foo<T>() T : IWeather { 
        if (typeof(T)==typeof(Rain))
           return new Rain();
        if (typeof(T)==typeof(Snow))
           return new Snow();
        Throw new ArgumentException("Not implemented for " + typeof(T).Name);
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM