簡體   English   中英

將對象投射到列表 <T> 能夠使用擴展方法

[英]Casting an object to List<T> to be able to use extension methods

我已經生成了一個擴展方法,以一種有意義的方式顯示IList<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的方法,該object也可以是列表。 因此,如果對象不是列表, ToString()使用ToString()方法;如果對象是列表, 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]>創建一個語句。 有沒有一種方法可以使用擴展方法或任何其他方式從任何類型的列表中獲取自定義字符串?

好吧,您基本上是在尋找運行時過載解決方案- dynamic做到了。 對於所需的單獨重載,有一個單獨的方法,它將根據通常的重載解決規則選擇正確的方法:

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

然后,您可以通過執行以下操作來調用最佳過載(在運行時)

Do((dynamic)someObject);

如果您有能力使用dynamic ,那么這可能是最簡單的解決方案。

如果沒有,您可以利用類型差異。 可以使用IEnumerable<T>代替IList<T> ,它對於T是協變的。 這可以讓你做

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

當然,這僅在ToDisplay方法沒有實際使用type參數的情況下才有用。

另外,正如InBetween正確指出的那樣, 值類型不支持方差 -因此對於List<int>仍然會失敗,例如- (new List<int>() is IEnumerable<object>) == false 它僅適用於引用類型。 在這種情況下,您仍然可以使用非通用IList ,但是您需要一個單獨的ToDisplay方法。 或者,您可以使用反射直接使用正確的泛型類型參數來調用正確的方法,但是到那時,您將擁有類似於dynamic東西,但是笨拙。

您基本上有兩種選擇:切換到使用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)

就我個人而言,我會走第一條路。 固然,它更容易閱讀和調試。

如果查看MSDN上List T定義 ,您會發現它實際上繼承了一個非通用的IList接口。

因此,您可以使用它來檢查您的對象是否正在實現此接口。 任何列表都將實現IList。

這意味着一件事,從您的ToDisplay方法中刪除通用參數。

另外,您可以使用is關鍵字來檢查您的對象是否在繼承樹中(請參見此處 )。

注意,要使用它,必須using System.Collections;添加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();
} 

您對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();
}

為什么不創建重載,將對象用作參數可能不是最好的選擇。

對於列表:

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

下面的示例使用反射來檢測類型是否為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
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM