C# 使用两个约束的Linq分组

C# 使用两个约束的Linq分组,c#,linq,C#,Linq,一个必要的免责声明:我希望帮助更好地理解Linq,所以我尝试在工作中解决一个实际问题,并使用Linq解决它。这并不意味着我假设Linq比更直接的过程解决方案更好(更快、更少的内存、更可读性等等)。如果有什么不同的话,我正试图找到Linq(和一种功能性风格)在我的作品中的优势 作为更大通信协议的一部分,我有一个列表。这些字节[]的大小是可变的,每个字节代表对某个系统的请求。这些字节[]必须按照与列表中相同的顺序发送到其他系统,但最好将多个字节[]请求打包到一条消息中,以减少通信开销。消息的有效负载

一个必要的免责声明:我希望帮助更好地理解Linq,所以我尝试在工作中解决一个实际问题,并使用Linq解决它。这并不意味着我假设Linq比更直接的过程解决方案更好(更快、更少的内存、更可读性等等)。如果有什么不同的话,我正试图找到Linq(和一种功能性风格)在我的作品中的优势

作为更大通信协议的一部分,我有一个
列表
。这些
字节[]
的大小是可变的,每个字节代表对某个系统的请求。这些
字节[]
必须按照与
列表中相同的顺序发送到其他系统,但最好将多个
字节[]
请求打包到一条消息中,以减少通信开销。消息的有效负载将是单个
字节[]
,它是所有单个
字节[]
的串联

关于
字节[]
的这种打包有两个限制。首先,我们对可以发送的总字节数有一个上限(
maxSendBytes
)。第二,每个请求将生成一个最大长度的可变长度响应。这些响应的最大长度之和不能大于另一个限制(
maxReceiveBytes
)。我们可以通过一个名为
longestreponselength
的函数来确定响应的最大长度,该函数接受请求的
byte[]

因此,理想的最终结果是将一个表示单个请求的
列表
转换为一个
列表
,其中每个
字节[]
是由一个或多个
字节[]
请求串联而成的单个消息有效负载

以下是我的程序性解决方案,可以帮助人们理解我的意图:

public static List<byte[]> PackList(List<byte[]> options, int maxSendBytes = 500, int maxReceiveBytes = 1500-200)
{
    var payloads = new List<byte[]>();      // each item is an outbound payload to put in a message
    var currentPayload = new List<byte>();  // payload we're accumulating on now
    var currentExpectedLength = 0;          // expected length of response payload

    foreach (var option in options)
    {
        var optionLength = LongestResponseLength(option);

        // add to current payload as long as neither limit hit

        if (currentPayload.Count + optionLength >= maxSendBytes ||
            currentExpectedLength + optionLength >= maxReceiveBytes)
        {
            payloads.Add(currentPayload.ToArray());     // add the payload to the list of payloads
            currentPayload = new List<byte>();          // start a new payload
            currentExpectedLength = 0;                  //   "
        }

        currentPayload.AddRange(option);
        currentExpectedLength += optionLength;
    }

    if (currentPayload.Count > 0)
    {
        payloads.Add(currentPayload.ToArray());
    }

    return payloads;
}
publicstaticlistpacklist(列表选项,int-maxSendBytes=500,int-maxReceiveBytes=1500-200)
{
var payloads=new List();//每个项目都是要放入消息中的出站负载
var currentPayload=new List();//我们正在累积的有效负载
var currentExpectedLength=0;//响应负载的预期长度
foreach(期权中的var期权)
{
var optionLength=长响应长度(选项);
//只要两个限制都未命中,则添加到当前有效负载
如果(currentPayload.Count+optionLength>=maxSendBytes||
currentExpectedLength+optionLength>=maxReceiveBytes)
{
payloads.Add(currentPayload.ToArray());//将有效负载添加到有效负载列表中
currentPayload=new List();//启动新的有效负载
currentExpectedLength=0;//“
}
currentPayload.AddRange(选项);
currentExpectedLength+=可选长度;
}
如果(currentPayload.Count>0)
{
Add(currentPayload.ToArray());
}
返回有效载荷;
}

有没有更好的方法来表达上述代码?可能吧。但这不是问题。

我很失望,如果不依靠
GroupBy
声明中的副作用,我无法解决这个问题,但我认为这满足了你的要求:

var rnd = new Random(0);
//make a list of mock messages
var individualMessages = Enumerable
    .Range(0, 1000)
    .Select(_ => Enumerable
        .Range(0, rnd.Next(1,100))
        .Select(__ => (byte)rnd.Next(255))
        .ToArray())
    .ToList();

var maxMessageSize = 1000;

var total = 0;
var groupingNum = 0;
var aggregatedMessages = individualMessages
    .GroupBy(x => {
        total += x.Length;
        if(total > maxMessageSize){
            groupingNum++;
            total = x.Length;
        }
        return groupingNum;
    })
    .Select(x => x.SelectMany(v => v).ToArray())
    .ToList();

您的问题有一点
tl/dr
问题,我建议您为尝试过程解决方案和尝试基于LINQ的解决方案添加一些代码。我认为如果您这样做,这可能是一个很好的问题。您不能将所有
位[]连接起来吗
然后只需使用
获取
获取任意多的位?好吧,是的,我会去除代码中所有工作专有的内容,但我想避免人们将代码拆分并对其进行评论。我会稍后再做。如果你问我,任何需要保持状态的关键问题都最好写出来作为一个生成器,而不是试图将其分解为使用LINQ的限制条件。任何依赖于副作用的LINQ查询都是您应该避免的查询类型。感谢您的建议,Kirk。希望这能更清楚地说明我在尝试做什么。好吧,这无疑增加了我对LINQ的理解,但似乎没有什么效果这与我的问题陈述有很大关系。