铸造一个目的是列表,以便能够使用扩展方法

问题描述:

我已生成的扩展方法以有意义的方式来显示IList<T>铸造一个目的是列表<T>,以便能够使用扩展方法

public static class IListExtension 
{ 
    public static string ToDisplay<T>(this IList<T> source, string seperator = ",") 
    { 
    string display = string.Empty; 
    // create the display string from the elements of this list 
    return display; 
    } 
} 

要使用该扩展我可以这样做:

IList<someClass> myList = CreateList(); 
myList.ToDisplay(); 

我的问题是现在,我有一个方法得到一个object,它也可以是一个列表。所以,如果对象没有列表,我想使用ToString()方法,如果它是一个列表,我想使用我的扩展。

public string CreateDisplayString(object element) 
{ 
    if (element == null) 
    return string.Empty; 

    if (element as IList<T> != null) 
    return (element as IList<T>).ToDisplay(); 

    return element.ToString(); 
} 

显然,上面的代码不工作,因为我不能强制转换为普通IList<T>。如果我使用现有的类,如IList<string>它适用于像我想要的字符串列表,但当然我不想为每个可能的IList<[name of class]>创建一个语句。有没有办法使用扩展方法或任何其他手段从任何类型的列表中获取自定义字符串?

+3

它的鸡/蛋的东西。如果没有'T',你不知道该把它扔到什么地方,也不知道该把它扔给你什么时候不能施放它。你能否将你的扩展添加到非泛型'IList'(它是由'List '实现的)?我想这个问题的答案取决于你在你的问题中“从这个列表的元素创建显示字符串”的实现! – Jamiec

如果你看看List T definition on MSDN,你可以观察到它实际上继承了一个非泛型的IList接口。

所以你可以用它来检查你的对象是否正在实现这个接口。任何列表将实施IList。

这意味着一件事,从您的ToDisplay方法中删除泛型参数。

此外,您可以使用is关键字来检查您的对象是否在继承树(ref here)中。

注意,使用它,你必须添加using System.Collections;

public static class IListExtension 
{ 
    public static string ToDisplay(this IList source, string seperator = ",") 
    { 
    string display = string.Empty; 
    // create the display string from the elements of this list 
    return display; 
    } 
} 

public string CreateDisplayString(object element) 
{ 
    if (element == null) 
    return string.Empty; 

    if (element is Ilist) 
    return (element as IList).ToDisplay(); 

    return element.ToString(); 
} 
+0

删除通用参数是解决方案,谢谢:) – Aaginor

为什么不创建一个重载,使用对象作为参数可能不是最好的事情。

对于列表:

public string CreateDisplayString<T>(IList<T> element) 
{ 
    if (element == null) 
    return string.Empty; 

    return element.ToDisplay(); 
} 

字符串:

public string CreateDisplayString(string element) 
{ 
    if (element == null) 
    return string.Empty; 

    return element.ToString(); 
} 
+0

除非您使用动态调用(它是运行时类型,而不是静态类型),否则这将不起作用。 –

好吧,你基本上是寻找运行时重载 - dynamic做到这一点。有你想要的单独重载一个单独的方法,它会选择基于通常的重载决策规则是正确的:

void Do<T>(IList<T> list) => ... 
void Do(string someString) => ... 
void Do(object somethingElse) => ... 

然后就可以调用最好的过载(在运行时)只是做

Do((dynamic)someObject); 

如果您可以负担得起使用dynamic,这可能是最简单的解决方案。

如果不是,您可以利用类型差异。而不是IList<T>,您可以使用IEnumerable<T>(这似乎足以适用于您所示的情况),它与T协作。这允许你做

if (someObject is IEnumerable<object> e) 
{ 
    e.ToDisplay(); 
} 

这是唯一有用的,如果ToDisplay方法实际上并不使用类型参数的东西,当然。

此外,作为插图中正确地指出,值类型不支持方差 - 所以这仍然会失败List<int>,例如 - (new List<int>() is IEnumerable<object>) == false。它仅适用于参考类型。对于这种情况,您仍然可以使用非通用的IList,但您需要为此单独使用ToDisplay方法。或者,您可以使用反射来直接调用正确的泛型类型参数的正确方法,但到那时,您只需要有一些与dynamic类似的东西,但更笨拙。

+0

如果'T'是一个值类型,'IEnumerable ''hack'将会失败;当涉及到结构时,不允许有任何变化。 – InBetween

+0

@InBetween好点,我会补充说。 – Luaan

+0

非常感谢您的全面回答!我之前从未使用动态,所以我会仔细研究一下。我认为最好和最简单的解决方案是使用IList作为扩展方法,因为我只是使用.ToString()方法来构建显示。 – Aaginor

基本上有两种选择:切换到使用IEnumerable而不是IList<T>,因为每个IList<>也是IEnumerable。然后,您可以使用asis来确定要采用哪个代码路径,根据需要投射以调用适当的方法。

您的其他选项是使用动态方法调用,它允许您根据object变量引用的运行时类型调用方法,例如,

object o = ...; 
SomeMethod((dynamic) o); 

private void SomeMethod<T>(IList<T> list) { ... } 
private void SomeMethod(string str) { ... } 

虽然,我的头顶,我不记得您是否可以动态调用带有类型参数的方法。如果没有,您必须将SomeMethod<T>(IList<T> list)替换为SomeMethod(IEnumerable e)

就我个人而言,我会走第一条路。虽然更粗糙,但更容易阅读和调试。

以下示例使用反射来检测,如果该类型是一个IList所以这将是安全的强制转换为IList的。

var lst = new List<SomeObject>(); 
var obj = lst; 

var type = obj.GetType(); 
var ilistType = typeof(IList<>); 


if(type == ilistType || type.GetInterfaces().Any(c=>c.IsGenericType && c.GetGenericTypeDefinition() == ilistType)){ 
Console.WriteLine("object is a list"); 
// code here 
} 

T对T有一些限制吗?如果不是,你可以使用非通用IList

public static string ToDisplay(this IList source, string seperator = ",") 
{ 
    StringBuilder str = new StringBuilder(); 
    foreach (var o in source) 
    { 
     if (o != null) 
     { 
      if (str.Length > 0) 
       str.Append(seperator); 

      IList list = o as IList; 
      if (list != null) 
       str.Append(list.ToDisplay(seperator)); 
      else 
       str.Append(o); 
     } 
    } 
    return str.ToString().TrimEnd(); 
} 
+0

T.好和简单的解决方案没有限制,非常感谢! – Aaginor