动态转换为IEnumerable <t>或T </t>

时间:2013-05-29 20:41:14

标签: c# generics reflection

我有一个接受T的类,并且有一个方法需要HttpResponseMessage并修改响应中的内容。

public class MyClass<T> {

    HttpResponseMessage Modify(HttpResponseMessage response) {
        T content;
        if(response.TryGetContentValue(out content)) {
            DoSomethingWithContent(content);
        }
        return response;
    }

    public void DoSomethingWithContent(T content) {}

    public void DoSomethingWithContent(IEnumerable<T> content) {}
}

像这样使用......

只要响应内容值的类型为T,但有时为IEnumerable<T>,这种情况就有效,而当TryGetContentValue()的情况下,T将返回false {{1} }}不是IEnumerable<T> 。所以我为DoSomethingWithContent创建了一个重载,但我正在努力找出一种有效的方法来动态转换或声明content到正确的类型,以便调用正确的重载。

答案

我选择了recursive,但我想发布完整的参考方法:

public class MyClass<T> {

    HttpResponseMessage Modify(HttpResponseMessage response) {
        object content;
        if(response.TryGetContentValue(out content)) {
            if(content is IEnumerable<T>)
                DoSomethingWithContent((IEnumerable<T>)content);
            else
                DoSomethingWithContent((T)content);
        }
        return response;
    }

    public void DoSomethingWithContent(T content) {}

    public void DoSomethingWithContent(IEnumerable<T> content) {}
}

5 个答案:

答案 0 :(得分:7)

您可以使用is来确定要使用的案例。

if (content is IEnumerable<T>)
    DoSomethingWithContent((IEnumerable<T>)content);
else
    DoSomethingWithContent(content);

如果你感觉很时髦,你可以通过强制转换来使用运行时绑定器,但不建议这样做。

DoSomethingWithContent((dynamic)content);

答案 1 :(得分:2)

IEnumerable<out T> 协变,所以如果你只是......

var enumerable = content as IEnumerable<T>;
if (enumerable == null)
    DoSomethingWithContent(content);
else
    DoSomethingWithContent(enumerable);

...即使DoSomethingWithContent(IEnumerable<T> content)的实际运行时类型为contentIEnumerable<C>继承C,它仍然会调用T。< / p>


这不能解决为什么你首先在IEnumerable<T>中尝试“适合”T

答案 2 :(得分:1)

是的,您的设计存在圆形问题。如果MyClass<T>被初始化为例如MyClass<List<string>>,那么T = List<string>,因此实际上不会调用重载的定义。您实际需要做的只是使用单个方法,但有一个子句来测试T是否实现IEnumerable<T>。你可以这样做;

if (content is IEnumerable<T>)

我不完全确定它会起作用,因为你甚至可能必须指定IEnumberable使用的类型。如果是这种情况,那么检查它是否实现了非通用IEnumberable,因为实现通用版本的所有集合也实现了非通用版本。

答案 3 :(得分:1)

这项工作了吗?

HttpResponseMessage Modify(HttpResponseMessage response) {
        T content;
        if(response.TryGetContentValue<T>(out content)) {
            DoSomethingWithContent(content);
        }
        else 
        {
           IEnumerable<T> content;
           if(response.TryGetContentValue<IEnumerable<T>>(out content)) {
            DoSomethingWithContent(content);
           }
        }
        return response;
    }

答案 4 :(得分:1)

假设DoSomethingIEnumerable<T>T是相同的(例如,一个委托给另一个) - 我认为总是使用一个更有意义IEnumerable<T> - 毕竟,T只是IEnumerable<T>,只有1个值。

当然,这可以回避TryGetContentValue只会给你一个T而不是IEnumerable<T>的问题。您可以先尝试T然后再回到IEnumerable<T>来处理此问题。我认为把它拉出作为一个对象并自己处理演员也可以。

所以 - 我认为你最终会得到类似的东西:

public class MyClass<T> {
    HttpResponseMessage Modify(HttpResponseMessage response) {
        IEnumerable<T> content = this.GetContent(response);
        DoSomethingWithContent(content);
        return response;
    }

    private IEnumerable<T> GetContent(HttpResponseMessage response) {
        object content;
        if (!response.TryGetContentValue(out content)) return new T[] { };
        if (content is T) return new T[] { (T)content };
        return content as IEnumerable<T> ?? new T[] { };
    }

    public void DoSomethingWithContent(IEnumerable<T> content) {
        foreach (var t in content) {
          // blah, blah
        }
    }
}

如果DoSomethingWithContent(T)DoSomethingWithContent(IEnumerable<T>)实际上不同 - 那么您基本上遵循与GetContent相同的模式来做出分支决策。