使用LINQ提取特定类型的列表元素

3
我有一个对象列表,并且我需要编写一个方法,根据输入参数(一个Type数组),提取一些元素。该函数必须返回一个元素数组,其中每个元素都是Type数组中的一个元素的实例。同时,这些元素只有在它们全部存在于列表中时才能从容器列表中删除。使用Type.IsInstanceOfType(o)方法进行类型比较。
class A {}
class B : A {}
class C : A {}
class D : A {}

public static A[] ExtractElements (List<A> list, Type[] specifiers) {...}

Type[] specifiers1 = new Type[2] {typeof(D), typeof(B)};
Type[] specifiers2 = new Type[3] {typeof(C), typeof(A), typeof(D)};
Type[] specifiers3 = new Type[2] {typeof(A), typeof(A)};
Type[] specifiers4 = new Type[2] {typeof(C), typeof(C)};

List<A> list = new List<A> {new B(), new A(), new D(), new C(), new A()};
A[] result1 = ExtractElements (list, specifiers1);

list  = new List<A> {new B(), new A(), new D(), new C(), new A()};
A[] result2 = ExtractElements (list, specifiers2);

list  = new List<A> {new B(), new A(), new D(), new C(), new A()};
A[] result3 = ExtractElements (list, specifiers3);

list  = new List<A> {new B(), new A(), new D(), new C(), new A()};
A[] result4 = ExtractElements (list, specifiers4);

这段代码的结果将会是:
  result1 is {D, B}, list is {A, C, A}  
  result2 is {C, B, D}, list is {A, A}  
  result3 is {B, A}, list is {D, C, A}  
  result4 is empty array, list is {B, A, D, C, A}  

作为一个独立的努力,是否可能编写一个类似的ExtractElements方法,只返回非空数组,如果列表包含所请求类型的项目,并且它们在列表中的顺序与类型输入数组中元素的顺序相对应,如下所示。
Type[] specifiers5 = new Type[2] {typeof(B), typeof(D)};
Type[] specifiers6 = new Type[2] {typeof(C), typeof(B)};

List<A> list = new List<A> {new B(), new A(), new D(), new C(), new A()};
A[] result5 = ExtractElements (list, specifiers5);

list  = new List<A> {new B(), new A(), new D(), new C(), new A()};
A[] result6 = ExtractElements (list, specifiers6);

该代码片段的结果将是:
result5 is {B, D}, list is {A, C, A}  
result6 is empty array, list is {B, A, D, C, A}  

我知道使用LINQ可以实现这个功能,但是很不幸的是我没有相关经验。

3个回答

1

我认为你可能说对了。删除我的答案。由于某种原因,我害怕使用那种方法,但一直知道它。我在考虑调用Enumerable.Cast<T>,但我知道这可能会在运行时出现问题。 - A-Dubb
哦,等等,唯一的问题是他想要检查多种类型。 - A-Dubb
啊,没错。这确实会让事情变得复杂。(我给你的答案点了赞) - hometoast

1
怎么样:

public IEnumerable<TType> ExtractElements<TType>(IEnumerable<TType> list, IEnumerable<Type> specifiers) {
   var specifiersList = specifiers.ToList();

   return list.Where(t => specifiersList.Any(s => s.IsAssignableFrom(t.GetType())));
}

var specifiers5 = new[] {typeof(B), typeof(D)};
var list = new List<A> {new B(), new A(), new D(), new C(), new A()};

// you can call ToArray() if you want but ForEach won't be available on that
// and you'll need a standard foreach() loop
var result5 = ExtractElements(list, specifiers5).ToList();

result5.ForEach(Console.WriteLine);

欲了解关于Type.IsAssignableFrom()更多信息,请点击链接。


看起来可以解决问题。我有一个带有工作示例的控制台应用程序。 - A-Dubb

0

这应该能够满足您的所有需求。我在ExtractElements方法中添加了一个可选参数,让您可以启用/禁用顺序匹配。

public static A[] ExtractElements (List<A> list, Type[] specifiers, bool orderMatters = false)
{
    var allFound = true;
    var listBackup = list.ToList(); // Make a backup copy   
    var returnList = new List<A>();
    var earliestMatch = 0;

    foreach (var spec in specifiers)
    {
        var item = list.FirstOrDefault (i => spec.IsAssignableFrom(i.GetType()));
        if (item != null)
        {
            var matchPosition = list.IndexOf(item);
            if (orderMatters && matchPosition < earliestMatch)  // we have an out of order match
            {
                allFound = false;
                break;
            }
            earliestMatch = matchPosition;
            list.Remove(item);
            returnList.Add(item);
        }
        else
        {
            allFound = false;
            break;
        }
    }

    if(!allFound)
    {
        // Can't just assign list to listBackup because we have to update the 
        // underlying values not the reference that was passed to the function.
        list.Clear();
        listBackup.ForEach(i => list.Add(i));
        returnList.Clear();
    }

    return returnList.ToArray();
}

我建议你下载LinqPad来帮助测试任何LINQ语句并学习LINQ的基础知识。

希望这可以帮到你。


哦,哇...我刚意识到他想要修改传入的集合。我就让你接着处理吧。 - A-Dubb
是的,那让情况变得更加尴尬了。 - Brent Stewart

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接