Casting return value to a generic type
Asked Answered
N

7

15

Suppose we have an interface with a single generic method:

public interface IExtender
{
    T GetValue<T>(string tag);
}

and a simple implementation A of it that returns instances of two different types (B and C) depending on the "tag" parameter:

public class A : IExtender
{
    public T GetValue<T>(string tag)
    {
        if (typeof(T) == typeof(B) && tag == null)
            return (T)(object) new B();
        if (typeof(T) == typeof(C) && tag == "foo")
            return (T)(object) new C();
        return default(T);
    }
}

is it possible to avoid the double cast (T)(object)? Or, is there a way to tell the compiler "hey, I am sure that this cast won't fail at runtime, just let me do it without first casting to object!"

Neediness answered 19/7, 2012 at 7:18 Comment(2)
Why you need (T)(Object) conversion? You can directly (T) new C() right?Stoeber
@Anuraj: No - that's the whole point of the question. Please read the blog post referenced in my answer.Cleotildeclepe
C
12

Or, is there a way to tell the compiler "hey, I am sure that this cast won't fail at runtime, just let me do it without first casting to object!"

No, the language is deliberately designed to prevent this. Eric Lippert blogged about this recently. I agree it's annoying, but it does make a certain kind of sense.

To be honest, "generic" methods like this are usually a bit of a design smell. If a method has to have special cases for various different types, you should at least consider using separate methods instead. (GetB, GetC)

Cleotildeclepe answered 19/7, 2012 at 7:21 Comment(5)
Yup. GetB, GetC and GetEtc aren't an option here but I don't know if this is the right place to discuss the design at length. Anyway, the first paragraph and the link are the answer I was looking for. Thanks.Neediness
Yes it is poossible, in more than one way, and sometimes it is the only good soluition to a design problem. Which is why the dynamic keyword was introduced. And Convert.ChangeType.Timeworn
@DinoDini: I don't think either of those is actually doing what the OP is talking about. The OP doesn't want to perform any actual value conversion - they just want to avoid the double cast. Using dynamic effectively just makes the conversion to object implicit, but it doesn't remove it in any helpful way.Cleotildeclepe
Blog link has gone dead, but I found it here ericlippert.com/2012/07/10/696Aalborg
@LouiseEggleton: Thanks, I've edited it now. For future similar situations, feel free to suggest the edit directly.Cleotildeclepe
R
6
public T MyMethod<T>(string tag) where T : class
    {
        return new A() as T;
    }
Riccio answered 19/7, 2012 at 7:28 Comment(2)
exactly what I was looking for!Hannie
This wouldn't work without restricting T to be a class. The other solution (below) with dynamic keyword does not have such limitation.Discount
A
3

check this sample:

    public T GetValue<T>(string tag) where T : class, new()
    {
        if (typeof(T) == typeof(B) && tag == null)
            return new T();
        if (typeof(T) == typeof(C) && tag == "foo")
            return new T();
        return default(T);
    }

no cast needed, you can create instance of "T", just add the generic constraint that saying that T is a class and it have parameterless constructor so you don't need to create another base types and you can be sure that only suitable types will go through this generic method.

Alkyd answered 19/7, 2012 at 7:26 Comment(1)
Well, this is what happens when you (me, really) try to simplify the code to post as example. In the real code the instances already exist and I can't put any constraint on T. So yes, this works (thanks!) but doesn't solve my problem.Neediness
T
2

You can use dynamic to store your real result, but you have to be sure the generic argument type is the right type you return.

TResult GetResult<TResult>() 
{
    dynamic r = 10;
    return r;
}
Trevino answered 17/10, 2018 at 20:51 Comment(3)
The actual answer. At the bottom of the page. Again.Timeworn
This doesn't really remove the cast - it just makes it implicit instead of explicit. It's also likely to perform rather less well than the double cast.Cleotildeclepe
This did work as a workaround for me. If the programmer checks the type themselves, I don't see the issue.Dispersoid
O
0

No, that is not possible. The only way to do so would be to let the compiler know about additional assumptions on T. As evidenced by the list of generic parameter constraints, there is no constraint defined in C# to require availability of a specific cast.

Onetoone answered 19/7, 2012 at 7:22 Comment(1)
It is possible with dynamic casting.Timeworn
C
0

if you let B and C implement the same interface you could use a type constraint on your T. Probably not exactly what you want, but as suggested also by others what you want is not really possible.

public interface IclassBndC {}

public class B : IclassBandC {}

public class C : IclassBandC {}

public class A : IExtender
{
    public T GetValue<T>(string tag) where T : IclassBandC 
    {
        if (tag == null)
            return new B();
        if (tag == "foo")
            return new C();
        return default(T);
    }
}
Critta answered 19/7, 2012 at 7:24 Comment(3)
Note that this requires that you can modify B and C.Onetoone
@O.R.Mapper true, did I miss a mentioning of that not being possible?Critta
You didn't, but there was no mention that B and C are written by the OP rather than pre-defined and sealed, either. Just thought the restriction should be mentioned.Onetoone
S
-1

we can do the type conversion in try and in catch can return the default value

public static T GetFirstHeaderValueOrDefault<T>(this HttpResponseMessage response, string headerKey)
{
    var toReturn = default(T);
    if (response != null && response.Headers != null && !string.IsNullOrWhiteSpace(headerKey) && response.Headers.TryGetValues(headerKey, out IEnumerable<string>? headerValues))
    {
        var valueString = headerValues?.FirstOrDefault();
        if (!string.IsNullOrWhiteSpace(valueString))
        {
            try
            {
                return (T)Convert.ChangeType(valueString, typeof(T));
            }
            catch (Exception)
            {
                return toReturn;
            }
        }
    }

    return toReturn;
}
Sheepdip answered 4/7, 2024 at 11:52 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.