2012-12-21 33 views
0

我有一個List<object>,它是各種類型的對象的集合。如何從列表中返回特定類型的對象<object>

我正在寫一個幫助器方法,它將返回一個特定類型的對象。輔助方法將接受類型名稱作爲字符串參數。

注意:我使用的是3.5框架。

+1

提供的類型名稱是否與每個對象的實際類型匹配,還是要將該對象轉換爲該類型的實例? –

+0

我想你想.OfType - http://msdn.microsoft.com/en-us/library/vstudio/bb360913(v=vs.90).aspx或.Cast Matt

+0

你想要的返回值輔助方法也是強類型的,或者只是簡單地返回適當類型的對象作爲'對象'可接受? – CodingGorilla

回答

8

如果您需要使用字符串作爲參數,不能依賴OfType<T>()擴展方法。幸運的是這很容易效仿

public IEnumerable<object> OfType(this List<object> list, string typeName) 
{ 
    return list.Where(x => x != null && x.GetType().Name == typeName); 
} 

正如評論所指出的@ChrisSinclair這種解決方案不管理的轉換,演員和繼承/接口。強制轉換(由於用戶定義的轉換運算符)和轉換(因爲TypeConverter s和IConvertible接口)稍微有點棘手。對於簡單的(隱含的)管型(如與繼承和接口),您可以使用此:

public IEnumerable<object> OfType(this List<object> list, string typeName) 
{ 
    Type type = Type.GetType(typeName); 
    return list.Where(x => x != null && type.IsAssignableFrom(x.GetType())); 
} 

如何執行在運行時轉換(甚至通過自定義轉換操作符)

我發現我需要類似於我在此答案中發佈的代碼,但我必須稍微擴展一下,這裏有一個更好的實現,可以處理自定義強制轉換和轉換。

一個 CastExtensions類(或更新的代碼,如果你不)內

將所有的東西,然後宣佈這個小enum其選項:

[Flags] 
public enum CastOptions 
{ 
    None = 0, 
    ExcludeNulls = 1, 
    UseConversions = 2 
} 

問題是C#,一般是靜態類型語言,這意味着在編譯時幾乎所有的東西(關於類型)都是已知的(然後在編譯時執行一個你必須知道的類型)。該函數處理簡單的個案(如派生)和更復雜的個案(界面,自定義轉換操作符 - 強制 - 以及需要時的轉換)。

public static IEnumerable<object> OfType(this List<object> list, 
             string typeName, CastOptions options) 
{ 
    Type type = Type.GetType(typeName); 

    foreach (var obj in list) 
    { 
     if (Object.ReferenceEquals(obj, null)) 
     { 
      if (options.HasFlag(CastOptions.ExcludeNulls)) 
       continue; 

      yield return obj; 
     } 

     var objectType = obj.GetType(); 

     // Derived type? 
     if (type.IsAssignableFrom(objectType)) 
      yield return obj; 

     // Should we try to convert? 
     if (!options.HasFlag(CastOptions.UseConversions)) 
      continue; 

     // Castable? 
     object convertedValue = null; 

     try 
     { 
      var method = typeof(CastExtensions) 
       .GetMethod("Cast", BindingFlags.Static|BindingFlags.NonPublic) 
       .MakeGenericMethod(type); 

      convertedValue = method.Invoke(null, new object[] { obj }); 
     } 
     catch (InvalidCastException) 
     { 
      // No implicit/explicit conversion operators 
     } 

     if (convertedValue != null) 
      yield return convertedValue; 

     // Convertible? 
     if (options.HasFlag(CastOptions.UseConversions)) 
     { 
      try 
      { 
       IConvertible convertible = obj as IConvertible; 
       if (convertible != null) 
        convertible.ToType(type, CultureInfo.CurrentCulture); 
      } 
      catch (Exception) 
      { 
       // Exact exception depends on the source object type 
      } 
     } 
    } 
} 

注意轉換可爲或不等同於鑄造,實際上它取決於 實施和參與行動的確切類型(這就是爲什麼你 可以啓用或通過禁用該功能選項)。

這是在運行時需要投小助手功能:

private static T Cast<T>(object obj) 
{ 
    return (T)obj; 
} 

我們可能會發出在運行時代碼(我想即使使用表達式,但我沒有嘗試),但小輔助方法將生成我們需要的代碼(從一個對象到運行時類型已知的泛型)。請注意,如預期的值類型此功能不起作用,例如:

int a = 1; 
float a = Cast<float>(a); // Run-time error 

這是因爲(object)1不能轉換到別的比int(這是所有裝箱值類型真)。如果您使用C#4.0,則應將參數objobject更改爲dynamic,並且所有內容都按預期工作(適用於所有類型)。

+2

Anil沒有指定,但是這不會考慮繼承/接口。如果沒問題,一切都會更好,否則會變得更加複雜/緩慢。不要敲你的解決方案(+1),Anil知道。 –

+1

@ChrisSinclair你是對的,它不在問題中,所以我甚至沒有想過它! –

2

也許這樣的事情:

var ofTypeTypeA = myList.OfType<TypeA>(); 
+3

-1作爲字符串參數 – bluish

-1

您可以使用Enumerable.OfType

var input = new List<object>(); 
input.Add(1); 
input.Add("foo"); 
var bar = input.OfType<string>(); 
+3

與GeorgesD答案一樣,這不符合OP接受類型名稱作爲參數的要求。 – CodingGorilla

+0

這不是原來的問題。它最有可能在創建後進行編輯。 – eandersson

-3

您可以使用is運算符(或通過類型和檢查也使用的)。下面是使用IS操作符的例子:

foreach (var ctl in ControlsList) 
{ 
    if (ctl is CheckBox) 
     //Do this 
    else if (ctl is TextBox) 
     //DoThis 
} 

,並通過將類型爲字符串中的參數,你可以做類似的事情讓類型測試反對:

Type t = System.Type.GetType("System.Int32"); 
-1

我想你需要將從列表中提取的單個對象強制轉換爲強類型對象。而不是把所有的清單都投給它。否則使用List<MyType>

所以我會去這個:How to cast to a type in C#

0

乾淨的方法是強制用戶指定類型作爲類型以避免應用程序中的字符串鬆散。

然後,您可以使用泛型,並只使用您感興趣的類型。這也將允許調用方稍後使用IEnumerable時跳過轉換。

因此,不是這樣的:

List<object> newList = GetOfType(myList, "SomeObject"); 
// CAST!! 
SomeObject someObject = newList[0] as SomeObject; 
if (someObject != null) 
    // use object 

你只想做:

IEnumerable<SomeObject> newList = myList.OfType<SomeObject>(); 
foreach (SomeObject someObject in newList){ 
    // no cast neccessary 

這使得在未來的不敏感,如果你要重命名的類SomeObject(因爲重構工具會拿起類名稱而不是字符串)

相關問題