简体   繁体   中英

C# generics + polymorphism workaround?

So I've currently got a little collection of methods to wrap the Unity WWW class; HandleTexture, HandleText, HandleBytes... but it's a lot of copy-pasted waste. I'm trying to wrap it all up under a generic HandleResult method that hands things off to _HandleResult under the hood. At this point the C# compiler complains about being unable to statically type T to Texture (each of the lines with closure(...);

private IEnumerator _HandleResult<T> (System.Action<T> closure, System.Action<string> onError){
    yield return webRequest;

    // Can't do a switch statement on Type, needs to be if/else chain :(
    if (SuccessWithoutErrors(onError)) {
        if(typeof(T) == typeof(Texture))
            closure(webRequest.texture);
        else if(typeof(T) == typeof(string))
            closure(webRequest.text);
        else if(typeof(T) == typeof(MovieTexture))
            closure(webRequest.movie);
        else if(typeof(T) == typeof(byte[]))
            closure(webRequest.bytes);
        else
            throw new System.NotSupportedException("Could not interpret response data as " + typeof(T).Name + ". Supported types include Texture, MovieTexture, byte[] and string.");
    }
}

I've been stuck at this point for a bit now. I'm fairly new to C# so maybe what I'm going for isn't feasible at all. Would greatly appreciate input from people who know more about the type system or what alternatives I should look into to achieve the same effect in terms of code duplication reduction.

C# doesn't seem to like it if you cast directly to a generic type. But you can cast to object first, then cast to the generic type:

private IEnumerator _HandleResult<T> (System.Action<T> closure, System.Action<string> onError){
    yield return webRequest;

    if (SuccessWithoutErrors(onError)) {
        if(typeof(T) == typeof(Texture))
            closure((T)(object)webRequest.texture);
        else if(typeof(T) == typeof(string))
            closure((T)(object)webRequest.text);
        else if(typeof(T) == typeof(MovieTexture))
            closure((T)(object)webRequest.movie);
        else if(typeof(T) == typeof(byte[]))
            closure((T)(object)webRequest.bytes);
        else
            throw new System.NotSupportedException("Could not interpret response data as " + typeof(T).Name + ". Supported types include Texture, MovieTexture, byte[] and string.");
    }
}

A cleaner option might be to supply a function to perform the property selection:

private IEnumerator _HandleResult<T> (System.Action<T> closure, Func<WebRequest, T> selector, System.Action<string> onError){
    yield return webRequest;

    if (SuccessWithoutErrors(onError)) { 
        closure(selector(webRequest)); 
    }
}

Then you could have a function signature and call site like the following:

void DoSomethingWithTexture(Texture t) {}

_HandleResult<Texture>(DoSomethingWithTexture, wr => wr.Texture, onError);

This second approach is cleaner and more flexible (what if you later want to add a Texture2 property to the object you're processing?) but requires an API change and may be more difficult to implement into existing code.

Try this:

    private IEnumerator _HandleResult<T>(object closure, System.Action<string> onError)
    {
        yield return webRequest;

        // Can't do a switch statement on Type, needs to be if/else chain :(
        if (true)
        {
            if (typeof(T) == typeof(Texture))
                ((System.Action<Texture>)closure)(webRequest.texture);
            else if (typeof(T) == typeof(string))
                ((System.Action<string>)closure)(webRequest.text);
            else if (typeof(T) == typeof(MovieTexture))
                ((System.Action<MovieTexture>)closure)(webRequest.movie);
            else if (typeof(T) == typeof(byte[]))
                ((System.Action<byte[]>)closure)(webRequest.bytes);
            else
                throw new System.NotSupportedException("Could not interpret response data as " + typeof(T).Name + ". Supported types include Texture, MovieTexture, byte[] and string.");
        }
    }

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