簡體   English   中英

僅當 LINQ 中有條件時才選擇

[英]Select only if condition in LINQ

我不確定我是否有這個 Q 的正確標題。無論如何,我正在嘗試將分隔字符串解析為 Enum 元素列表:

public enum MyEnum { Enum1, Enum2, Enum3 }

給定輸入:

string s = "Enum2, enum3, Foo,";

我只想輸出MyEnum中存在的部分(忽略大小寫):
[MyEnum.Enum2, MyEnum.Enum3]

IEnumerable<MyEnum> sl = 
    s.Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries)                         
    .Select(a => { if (Enum.TryParse(a, true, out MyEnum e)) return e; else return nothing ??? })

如果TryParse()失敗,如何從Select()返回“無”?
我可以做一件丑陋的事情,比如:

IEnumerable<MyEnum> sl = 
    s.Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries)                
    .Where(a => Enum.TryParse(a, true, out MyEnum dummy))
    .Select(a => Enum.Parse(typeof(MyEnum), a, true));

無緣無故地做兩次解析工作,
但我確定我錯過了一些微不足道的東西。
如何以一種高效而優雅的方式做到這一點?

如果您為enum使用可為空的值,這將非常簡單:

IEnumerable<MyEnum> sl =
    s
        .Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries)
        .Select(a => { if (Enum.TryParse(a, true, out MyEnum e)) return (MyEnum?)e; else return null; })
        .Where(x => x.HasValue)
        .Select(x => x.Value);

你甚至可以進一步減少它:

        .Select(a => Enum.TryParse(a, true, out MyEnum e) ? (MyEnum?)e : null)

或者您可以使用SelectMany並避免可空值:

IEnumerable<MyEnum> sl =
    s
        .Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries)
        .SelectMany(a => Enum.TryParse(a, true, out MyEnum e) ? new[] { e } : new MyEnum[] { });

即使Enum.TryParse做了繁重的工作,它也是次優的。 它可以產生假陽性結果。

例如,如果您的s字符串如下所示:

string s = "Enum2, enum3, Foo, 4";

然后Enum.TryParse將能夠將Enum2enum34轉換為有效的MyEnum實例。

為了從結果集中過濾掉4 ,您必須調用Enum.IsDefined 這是一個如何結合TryParseIsDefined

s.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries)
  .Where(value => Enum.TryParse<MyEnum>(value, ignoreCase: true, out var parsedValue) && Enum.IsDefined(typeof(MyEnum), parsedValue))
  .Select(value => (MyEnum)Enum.Parse(typeof(MyEnum), value, ignoreCase: true))
  .ToList();

您可以使用可用於以下情況的 LINQ 聚合方法:

var sl= s.Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries)
                .Aggregate(new List<MyEnum> (),(List<MyEnum> enums,string a)=>
                {
                    if (Enum.TryParse(a, true, out MyEnum e))
                    {
                        enums.Add(e);
                    }

                    return enums;

                });

您可以將值-1指定為無效的枚舉值,從而充當任何無效字符串的占位符,並允許它們隨后被排除。 此外,這避免了多次解析字符串。

var invalidEnum = (MyEnum) (-1);

var sl = s.Split(new char[] {',', ' '}, StringSplitOptions.RemoveEmptyEntries)
    .Select(a =>
    {
        var isValid = Enum.TryParse(a, true, out MyEnum myEnum);

        return isValid ? myEnum : invalidEnum;
    })
    .Where(a => a != invalidEnum);

如果明確定義,枚舉可能已經使用了-1 (與原始示例中默認最小值為零( 0 )不同,因此使用-1是可以的)。 為了最小化這種風險,可以使用以下值代替-1

var invalidEnum = (MyEnum) (int.MinValue);
// or
var invalidEnum = (MyEnum) (int.MaxValue);

如果仍然擔心這些極不可能的值被采用,那么可以找到枚舉未使用的下一個可用數字並將其分配為無效枚舉:

var invalidEnum = (MyEnum) int.MinValue;

foreach(var number in Enumerable.Range(int.MinValue, int.MaxValue))
{
    if (!Enum.IsDefined(typeof(MyEnum), number))
    {
        invalidEnum = (MyEnum) number;
        break;        
    }
}

將字符串 s 划分為子字符串后,您只想保留正確 myEnums 的子字符串的枚舉值。

您已經知道如何將原始字符串划分為子字符串:

string s = ...
var subStrings = .Split(new char[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries);

使用 Select 嘗試將每個 subString 解析為可空的 MyEnum。 如果失敗,則可空對象沒有值; 否則可空值具有 MyEnum 值。

刪除沒有值的可空值,並選擇剩余值的值:

var result = subStrings.Select(subString => new
{
    if (Enum.TryParse(subString, true out MyEnum parsedEnum)
    {
        return new Nullable<MyEnum>(parsedEnum);
    }
    else
    {
        return new Nullable<MyEnum>(null);
    }
})
.Where(nullableEnum => nullableEnum.HasValue);
.Select(nullableEnum => nullableEnum.Value);

暫無
暫無

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

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