c#可点数的.采用默认值

本文关键字:默认值 | 更新日期: 2023-09-27 18:04:50

在c#中从Enumerable中获得x值的最佳方法是什么?如果我像这样使用Enumerable .Take():

var myList = Enumerable.Range(0,10);
var result = myList.Take(20);

结果将只有10个元素。

我想用默认值填充缺失的条目。像这样:

var myList = Enumerable.Range(0,10);
var result = myList.TakeOrDefault(20, default(int));  //Is there anything like this?

c#中有这样的函数吗?如果没有,实现它的最佳方法是什么?

c#可点数的.采用默认值

你可以这样做:

var result = myList.Concat(Enumerable.Repeat(default(int), 20)).Take(20); 

很容易把它变成一个扩展方法:

public static IEnumerable<T> TakeOrDefault<T>(this IEnumerable<T> list, int count, T defaultValue)
{
    return  list.Concat(Enumerable.Repeat(defaultValue, count)).Take(count);
}

但是这里有一个微妙的问题。对于值类型,这将非常好地工作,对于引用类型,如果您的defaultValue不是null,您将多次添加相同的对象。这可能不是你想要的。例如,如果你有这样的:

var result = myList.TakeOrDefault(20, new Foo());

您将添加相同的Foo 实例来填充您的集合。要解决这个问题,您需要这样做:

public static IEnumerable<T> TakeOrDefault<T>(this IEnumerable<T> list, int count, Func<T> defaultFactory)
{
    return  list.Concat(Enumerable.Range(0, count).Select(i => defaultFactory())).Take(count);
}

你可以这样调用:

var result = myList.TakeOrDefault(20, () => new Foo())
当然,这两种方法可以共存,因此您可以轻松地使用:
// pad a list of ints with zeroes
var intResult = myIntList.TakeOrDefault(20, default(int));
// pad a list of objects with null
var objNullResult = myObjList.TakeOrDefault(20, (object)null);
// pad a list of Foo with new (separate) instances of Foo
var objPadNewResult = myFooList.TakeOrDefault(20, () => new Foo());

默认情况下不存在,但作为扩展方法编写它很容易

public static IEnumerable<T> TakeOrDefault<T>(this IEnumerable<T> items, int count, T defaultValue)
{
    var i = 0;
    foreach(var item in items)
    {
        i++;
        yield return item;
        if(i == count)
             yield break;
    }
    while(i++<count)
    {
        yield return defaultValue;
    }
}

实例:http://rextester.com/XANF91263

您正在寻找的是一个通用的PadTo方法,它可以在需要时使用给定值扩展集合的长度。

public static IEnumerable<T> PadTo<T>(this IEnumerable<T> source, int len)
{
    return source.PadTo(len, default(T));
}
public static IEnumerable<T> PadTo<T>(this IEnumerable<T> source, int len, T elem)
{
    return source.PadTo(len, () => elem);
}
public static IEnumerable<T> PadTo<T>(this IEnumerable<T> source, int len, Func<T> elem)
{
    int i = 0;
    foreach(var t in source)
    {
        i++;
        yield return t;
    }
    while(i++ < len)
        yield return elem();
}

你现在可以表达:

myList.Take(20).PadTo(20);

类似于Scala的List[A].padTo

您可以使用Concat来实现此目的。您可以使用一个简单的助手方法将所有这些连接在一起:

public IEnumerable<T> TakeSpawn(this IEnumerable<T> @this, int take, T defaultElement)
{
  return @this.Concat(Enumerable.Repeat(defaultElement, take)).Take(take);
}

这个想法是,您总是在原始可枚举对象的末尾添加另一个可枚举对象,因此,如果输入没有足够的元素,它将从Repeat开始枚举。

在。net框架中没有任何东西,据我所知没有。这可以通过使用扩展方法轻松实现,并且如果您自己提供默认值,它适用于所有类型:

public static class ListExtensions
{
    public static IEnumerable<T> TakeOrDefault<T>(this List<T> list, int count, T defaultValue)
    {
        int missingItems = count - list.Count;
        List<T> extra = new List<T>(missingItems);
        for (int i = 0; i < missingItems; i++)
            extra.Add(defaultValue);
        return list.Take(count).Concat(extra);
    }
}

我为此编写了一个快速扩展,它依赖于T是一个值类型。

  public static class Extensions
    {
        public static IEnumerable<T> TakeOrDefault<T>(this IEnumerable<T> list, int totalElements)
        {
            List<T> finalList = list.ToList();
            if (list.Count() < totalElements)
            {
                for (int i = list.Count(); i < totalElements; i++)
                {
                    finalList.Add(Activator.CreateInstance<T>());
                }
            }
            return finalList;
        }
    }

为什么不直接编写一个扩展方法来检查计数并返回剩余条目的默认值呢?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication3
{
    class Program
    {
        static void Main(string[] args)
        {
            List<int> values = new List<int>{1, 2, 3, 4};
            IEnumerable<int> moreValues = values.TakeOrDefault(3, 100);
            Console.WriteLine(moreValues.Count());
            moreValues = values.TakeOrDefault(4, 100);
            Console.WriteLine(moreValues.Count());
            moreValues = values.TakeOrDefault(10, 100);
            Console.WriteLine(moreValues.Count());
        }
    }
    public static class ExtensionMethods
    {
        public static IEnumerable<T> TakeOrDefault<T>(this IEnumerable<T> enumerable, int count, T defaultValue)
        {
            int returnedCount = 0;
            foreach (T variable in enumerable)
            {
                returnedCount++;
                yield return variable;
                if (returnedCount == count)
                {
                    yield break;
                }
            }
            if (returnedCount < count)
            {
                for (int i = returnedCount; i < count; i++)
                {
                    yield return defaultValue;
                }
            }
        }
    }
}