动态强制转换为IEnumerable<或T

本文关键字:IEnumerable 转换 动态 | 更新日期: 2023-09-27 18:15:52

我有一个接受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()将返回false,因为T不是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) {}
}

动态强制转换为IEnumerable<或T

您可以使用is来确定使用哪种情况

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

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

DoSomethingWithContent((dynamic)content);

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

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

…即使content的实际运行时类型是IEnumerable<C>,它仍然会调用DoSomethingWithContent(IEnumerable<T> content),其中C继承了T


这并没有解决为什么你在T中尝试"适合"IEnumerable<T> ?

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

if (content is IEnumerable<T>)

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

这个行吗?

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;
    }

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

当然,这回避了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相同的模式来做出分支决策。

相关文章: