我知道这很可能是一个涉及泛型的简单问题,或者很可能只是一个很大的“禁忌”整体,但我很想知道我是否可以实现这一点。
我正在尝试创建一个接收object
的方法,如果object
是一个可枚举的类型,则将其传递给可以处理任何类型的可枚举的方法,但是我得到了非常困难。
我接收对象的方法的当前代码如下所示:
private void MyMethod()
{
Dictionary<string, object> MyVals = new Dictionary<string,object>();
... fill the MyVals dictionary ...
object x = MyVals["Val_1"];
Type type = x.GetType();
// Check if we have a series of values rather than a single value
if (type != typeof(string) && typeof(IEnumerable).IsAssignableFrom(type))
Test(x);
}
然后,我认为我可以将某些写成以下其中一项作为我的Test
方法的方法签名:
1。把它写成好像是IEnumerable对象:
private void Test(IEnumerable<object> MyEnumeration)
尝试通过以下方式调用它:
Test((IEnumerable<object>)x);
导致无法从IEnumerable<int>
投射到IEnumerable<object>
2。尝试使用泛型:
private void Test<T>(IEnumerable<T> MyEnumeration)
尝试通过以下方式调用它:
Test(x);
导致签名错误/无效参数的设计时错误。
或通过:
Test<type>(x);
导致无法找到类型或命名空间type
的设计时错误。
如何做到这一点,或者只是糟糕的编程习惯,还有更好的方法吗?
谢谢!
答案 0 :(得分:2)
问题在于此代码:
if (type != typeof(string) && typeof(IEnumerable).IsAssignableFrom(type))
Test(x);
您现在知道x
是IEnumerable,但当编译器确定哪些方法签名兼容时,它仍被视为object
。
如果你这样做了:
if (type != typeof(string) && typeof(IEnumerable).IsAssignableFrom(type))
{
IEnumerable asEnumerable = (IEnumerable)x;
Test(asEnumerable);
}
然后可以传递给
void Test(IEnumerable t)
{
}
或者,如果您真的想使用IEnumerable<object>
:
if (type != typeof(string) && typeof(IEnumerable).IsAssignableFrom(type))
{
IEnumerable<object> asEnumerable = ((IEnumerable)x).Cast<object>();
Test(asEnumerable);
}
而且,如果您想要IEnumerable<T>
,请参阅Jon Skeet的answer以解决其他问题。
答案 1 :(得分:0)
这有一个糟糕的代码味道,所以我会详细说明你真正想做的事情,也许有人可以提供帮助。您的运行时错误是由于您拥有的集合实际上不是IEnumerable [object],而是IEnumberable [int]。你必须先打电话给.Cast。 所以这将是双重演员。
Test(((IEnumerable<int>)x).Cast<object>);
同样,这有一种可怕的代码味道。你应该详细说明数据是如何进入的,我相信有人可以帮助你。
答案 2 :(得分:0)
您应该创建两个Test()方法的重载。一个处理IEnumerable
而另一个处理IEnumerable<T>
。 为什么需要方法重载?因为IEnumerables
不是通用的IEnumerable<T>
,并且在运行时您不会知道要使用的泛型类型Cast
在object
。
以下是展示您如何实现所需目标的示例:
public void MyMethod()
{
// Sample object with underlying type as IEnumerable
object obj1 = new ArrayList();
// Sample object with underlying type as IEnumerable & IEnumerable<T>
object obj2 = (IList<string>)new List<string>();
if (typeof(IEnumerable).IsAssignableFrom(obj1.GetType()))
{
if (!obj1.GetType().IsGenericType)
{
// Handles case of IEnumerable
Test((IEnumerable)obj1);
}
else
{
// Handles case of IEnumerable<T>
InvokeGenericTest(obj2);
}
}
}
public void Test(IEnumerable source)
{
Console.WriteLine("Yes it was IEnumerable.");
}
public void Test<T>(IEnumerable<T> source)
{
Console.WriteLine("Yes it was IEnumerable<{0}>.", typeof(T));
// Use linq with out worries.
source.ToList().ForEach(x => Console.WriteLine(x));
}
/// <summary>
/// Invokes the generic overload of Test method.
/// </summary>
/// <param name="source"></param>
private void InvokeGenericTest(object source)
{
Type t = source.GetType();
var method = this.GetType().GetMethods().Where(x => x.IsGenericMethod && x.Name == "Test").First();
var genericMethod = method.MakeGenericMethod(t.GenericTypeArguments.First());
genericMethod.Invoke(this, new object[] { source });
}