C# 如果IEnumerable存在,我如何优雅地编写一个函数来从IEnumerable获取唯一属性?

C# 如果IEnumerable存在,我如何优雅地编写一个函数来从IEnumerable获取唯一属性?,c#,C#,我想写一个经过IEnumerable的函数。对于IEnumerable中的每个项,它都会获得一个枚举属性。如果IEnumerable中的所有内容对该属性具有相同的值,则返回该值。否则,它将返回null。我能做到,但不优雅。我可以使用Linq表达式吗?请参阅下面的UniqueOption函数 namespace Play { public enum Option { Tom, Dick, Harry } public class OptionHolder { pu

我想写一个经过IEnumerable的函数。对于IEnumerable中的每个项,它都会获得一个枚举属性。如果IEnumerable中的所有内容对该属性具有相同的值,则返回该值。否则,它将返回null。我能做到,但不优雅。我可以使用Linq表达式吗?请参阅下面的UniqueOption函数

namespace Play
{
public enum Option
{
    Tom,
    Dick,
    Harry
}

public class OptionHolder
{
    public Option Option { get; set; }

    public override string ToString()
    {
        return Option.ToString();
    }
}

public class Program
{
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    private static void Main()
    {
        Program p1 = new Program(Option.Tom, Option.Dick, Option.Harry);
        Console.WriteLine("1: "+p1.UniqueOption());     //should be null
        Program p2 = new Program(Option.Dick, Option.Dick, Option.Dick);
        Console.WriteLine("2: " + p2.UniqueOption());   //should be Dick
        Program p3 = new Program(Option.Harry);         
        Console.WriteLine("3: " + p3.UniqueOption());   //should be Harry
    }

    public Program(params Option[] options)
    {
        optionList = new List<OptionHolder>();
        foreach (Option option in options)
        {
            OptionHolder holder = new OptionHolder();
            holder.Option = option;
            optionList.Add(holder);
        }
    }

    /**
     * If all the OptionHolders in the Holders property have the same Option, return this.
     * Otherwise (there are no OptionHolders, or there is more than one but they hold different Options), return null.
     */
    public Option? UniqueOption()
    {
        Option? option = null;

        foreach(OptionHolder holder in optionList) {
            Option o = holder.Option;
            if (option == null)
            {
                option = o;
            }
            else if (option != o)
            {
                return null;
            }
        }
        return option;
    }

    private List<OptionHolder> optionList;

    public IEnumerable<OptionHolder> Holders
    {
        get { return optionList; }
    }

    public override string ToString()
    {
        return String.Join(",", optionList);
    }
}
名称空间播放
{
公共枚举选项
{
汤姆,
家伙,
骚扰
}
公共类期权持有人
{
公共选项{get;set;}
公共重写字符串ToString()
{
return Option.ToString();
}
}
公共课程
{
/// 
///应用程序的主要入口点。
/// 
[状态线程]
私有静态void Main()
{
程序p1=新程序(Option.Tom,Option.Dick,Option.Harry);
Console.WriteLine(“1:+p1.UniqueOption());//应为null
程序p2=新程序(Option.Dick,Option.Dick,Option.Dick);
Console.WriteLine(“2:+p2.UniqueOption());//应该是Dick
程序p3=新程序(选项.Harry);
Console.WriteLine(“3:+p3.UniqueOption());//应该是Harry
}
公共程序(参数选项[]选项)
{
optionList=新列表();
foreach(选项中的选项)
{
期权持有人=新的期权持有人();
持有人。期权=期权;
选择列表。添加(持有人);
}
}
/**
*如果持有人财产中的所有期权持有人都有相同的期权,则返回此项。
*否则(没有期权持有人,或者有多个期权持有人,但他们持有不同的期权),返回null。
*/
公共选项?唯一选项()
{
选项?选项=null;
foreach(期权列表中的期权持有人){
期权o=持有人期权;
如果(选项==null)
{
选项=o;
}
else if(选项!=o)
{
返回null;
}
}
返回选项;
}
私人列表选择列表;
公共数字持有者
{
获取{返回选项列表;}
}
公共重写字符串ToString()
{
返回字符串。Join(“,”选项列表);
}
}

}如果我理解正确,那么您可以使用Linq的
Distinct
方法

public Option? UniqueOption()
{
    var distinct = optionList.Select(x=> x.Option).Distinct();
    if(distinct.Count() == 1)
    {
        return distinct.First();
    }
    return null;
}

public Option? UniqueOptionOptimized()
{
    HashSet<Option> set = new HashSet<Option>();
    foreach (var item in optionList)
    {
        if (set.Add(item.Option) && set.Count > 1)
        {
            return null;
        }
    }

    if (set.Count == 1)
        return set.First();
    else
        return null;
}

public Option? UniqueOptionOptimized2()
{
    using(var distinctEnumerator = optionList.Select(x => x.Option).Distinct().GetEnumerator())
    {
        if(distinctEnumerator.MoveNext())
        {
            var firstOption = distinctEnumerator.Current;
            if(!distinctEnumerator.MoveNext())
                return firstOption;
        }
    }
    return null;
}
公共选项?唯一选项()
{
var distinct=optionList.Select(x=>x.Option).distinct();
if(distinct.Count()==1)
{
返回distinct.First();
}
返回null;
}
公共选择?UniqueOptionOptimized()
{
HashSet=newhashset();
foreach(选项列表中的var项目)
{
if(set.Add(item.Option)和set.Count>1)
{
返回null;
}
}
如果(set.Count==1)
返回set.First();
其他的
返回null;
}
公共选择?UniqueOptionOptimized2()
{
使用(var distinctEnumerator=optionList.Select(x=>x.Option).Distinct().GetEnumerator())
{
if(distinctEnumerator.MoveNext())
{
var firstOption=distinctEnumerator.Current;
如果(!distinctEnumerator.MoveNext())
返回优先选择权;
}
}
返回null;
}

以下功能应适合您的需要。然而,我尝试了一个更简单的应用程序,但你应该能够安排你想要的

    public Option? UniqueOption(params Option[] args)
    {
        if (args == null)
            return null;  //or maybe throws?

        var d = args.Distinct().ToArray();
        return d.Length != 1
            ? d[0]
            : new Nullable<Option>();
    }
公共选项?唯一选项(参数选项[]参数)
{
如果(args==null)
返回null;//或者可能抛出?
var d=args.Distinct().ToArray();
返回d.长度!=1
?d[0]
:new Nullable();
}
另一种变体:

public Option? UniqueOption()
{
    if(!optionList.Any()) return null;
    var first = optionList.First();
    return optionList.All(a => first.Option == a.Option) ? (Option?)first.Option : null;
}
使用
Distinct()
Take(2)
在找到两个不同的选项后立即停止枚举列表,然后检查是否恰好找到一个不同的选项(相对于零或两个):

更新:要检查实际枚举了多少值,可以使用以下帮助器方法:

public static IEnumerable<T> Trace<T>(IEnumerable<T> values)
{
    foreach (T value in values)
    {
        Console.WriteLine("Yielding {0}...", value);
        yield return value;
    }
}

这表明
p1
只枚举
Tom
Dick
,而不是
Harry

我最终使用了与我原来的解决方案没有太大区别的东西。不过,等我有更多时间的时候,我会再看看这个

public Option? UniqueOption()
{            
        IEnumerator<OptionHolder> enumerator = Holders.GetEnumerator();
        bool hasMoreElements;
        Option? result=null;
        do
        {
           hasMoreElements = enumerator.MoveNext();
           if (hasMoreElements)
           {
              Option? option = enumerator.Current.Option;
              result = (result != null && result != option) ? null : option; 
           }
        } 
        while (hasMoreElements && result != null);
        return result; 
} 
公共选项?唯一选项()
{            
IEnumerator枚举器=Holders.GetEnumerator();
布尔哈斯莫尔元素;
选项?结果=空;
做
{
hasMoreElements=enumerator.MoveNext();
if(hasMoreElements)
{
Option?Option=enumerator.Current.Option;
结果=(结果!=null&&result!=option)?null:option;
}
} 
while(hasMoreElements&&result!=null);
返回结果;
} 

如果
d.Length==0
d[0]
引发异常这与Sriram Sakthivel的解决方案具有相同的性能问题。此解决方案可以工作,但效率低下。如果有1000个元素,前两个元素的属性值不同,那么就没有必要对其他998个元素做任何处理。@PaulRichards:我相信你想让桶里的威士忌和妻子同时喝醉。Linq绝对不是高效的!要么你想要一个优雅和多功能的Linq方式,要么你选择一个优化但困难的方式。我的解决方案和Sriram的第一个解决方案都是Linq,其他的都不是。我意识到Linq并不以其效率而闻名@Mario Vernari。然而,Any方法不一定遍历每一项。我已经有了我的解决方案,但我会再考虑一下。@srramsakthivel:
Distinct(x=>x.Option)
不会编译,因为
Distinct
使用可选的
IEqualityComparer
,而不是选择器。您应该在中使用
Select(x=>x.Option).Distinct()
public static IEnumerable<T> Trace<T>(IEnumerable<T> values)
{
    foreach (T value in values)
    {
        Console.WriteLine("Yielding {0}...", value);
        yield return value;
    }
}
Option[] options = Trace(optionList).Select(holder => holder.Option).Distinct().Take(2).ToArray();
public Option? UniqueOption()
{            
        IEnumerator<OptionHolder> enumerator = Holders.GetEnumerator();
        bool hasMoreElements;
        Option? result=null;
        do
        {
           hasMoreElements = enumerator.MoveNext();
           if (hasMoreElements)
           {
              Option? option = enumerator.Current.Option;
              result = (result != null && result != option) ? null : option; 
           }
        } 
        while (hasMoreElements && result != null);
        return result; 
}