如何在 C# 或 Linq 中比较大于或小于运算符值的两个字节数组?
Posted
技术标签:
【中文标题】如何在 C# 或 Linq 中比较大于或小于运算符值的两个字节数组?【英文标题】:How to compare two byte arrays with greater than or less than operator value in C# or Linq? 【发布时间】:2017-07-19 01:31:27 【问题描述】:我的 Code First Entity Framework for SQL TimeStamps 中有一个字节数组,映射如下:
[Column(TypeName = "timestamp")]
[MaxLength(8)]
[Timestamp]
public byte[] TimeStamps get; set;
上述属性相当于C#中的SQL server“timestamp”数据类型。
在 SQL Server 中,我可以轻松地比较“时间戳”,如下所示...
SELECT * FROM tableName WHERE timestampsColumnName > 0x000000000017C1A2
我想在 C# 或 Linq Query 中实现相同的目标。在这里,我编写了我的 Linq 查询,但无法正常工作。
byte[] lastTimeStamp = someByteArrayValue;
lstCostCenter.Where(p => p.TimeStamps > lastTimeStamp);
我也试过用BitConverter
来比较一个同样不起作用的两字节数组...
lstCostCenter.Where(p => BitConverter.ToInt64(p.TimeStamps, 0) > BitConverter.ToInt64(lastTimeStamp, 0));
如何在 C# 或 Linq Query 中比较字节数组。
注意 - 我只是不想像使用 SequenceEqual 或任何其他仅比较并返回真或假的方法那样比较两个数组。我希望在 Linq 查询中与大于 > 或小于
【问题讨论】:
您想比较使用数据库(实体框架、LINQ to sql 等)LINQ 查询还是内存中 linq 查询? 字节在数组中是如何排列的?首先是最高有效字节?如果是这样,则尝试使用.Reverse().ToArray()
将输入字节反转到 BitConverter.ToInt64
调用
@Evk 我想与不在内存中的数据库 Linq 查询进行比较。
@Dilip0165 好的,我在回答中提供了一种使用实体框架的方法。
【参考方案1】:
一种方法是使用IStructuralComparable
,Array
隐式实现:
byte[] rv1 = new byte[] 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01 ;
byte[] rv2 = new byte[] 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x05 ;
var result = ((IStructuralComparable)rv1).CompareTo(rv2, Comparer<byte>.Default); // returns negative value, because rv1 < rv2
如果出于某种原因您想使用BitConverter
,则必须反转数组,因为BitConverter
在大多数体系结构上都是小端(为了安全起见-您应该检查BitConverter.IsLittleEndian
字段并仅在它返回true 时反转)。请注意,这样做效率不高。
var i1 = BitConverter.ToUInt64(rv1.Reverse().ToArray(), 0);
var i2 = BitConverter.ToUInt64(rv2.Reverse().ToArray(), 0);
现在,如果您使用 Entity Framework 并且需要比较数据库查询中的时间戳,情况会有所不同,因为 Entity Framework 会检查您的查询表达式以寻找它理解的模式。它不理解IStructuralComparable
比较(当然还有BitConverter
转换),所以你必须使用一个技巧。声明名称为Compare
的字节数组的扩展方法:
static class ArrayExtensions
public static int Compare(this byte[] b1, byte[] b2)
// you can as well just throw NotImplementedException here, EF will not call this method directly
if (b1 == null && b2 == null)
return 0;
else if (b1 == null)
return -1;
else if (b2 == null)
return 1;
return ((IStructuralComparable) b1).CompareTo(b2, Comparer<byte>.Default);
并在 EF LINQ 查询中使用它:
var result = ctx.TestTables.Where(c => c.RowVersion.Compare(rv1) > 0).ToList();
在分析时,EF 将看到名称为 Compare
和兼容签名的方法,并将其转换为正确的 sql 查询(从 RowVersion > @yourVersion 的表中选择 *)
【讨论】:
不知道Array
实现了IStructuralComparable
接口:-)
ArrayExtension 类的方法“Compare”解决了我的问题,但我可以将“Compare”方法重命名为“ByteArrayCompare”以获得更好的可读性吗?在 Linq 中,我们已经内置了“比较”方法。
不幸的是你不能,因为这就是 Compare 的名字,它使这个技巧完全有效。如果使用任何其他名称,它将因 NotSupportedException 而失败。请注意,您可以使用名称为 Compare 的任何兼容方法,参数不必是字节数组。例如“int Compare(this object b1, object b2)”也可以。
@Dilip0165 我已经更新了我上一条评论,也许你可以重用你现有的 Compare 方法。
@xanatos 它还实现了 IStructuralEquatable ,可以用来代替通常的 linq SequenceEqual。【参考方案2】:
如果你知道这两个字节数组长度相等并且首先是最重要的字节,那么这有效:
Func<byte[], byte[], bool> isGreater =
(xs, ys) =>
xs
.Zip(ys, (x, y) => new x, y )
.Where(z => z.x != z.y)
.Take(1)
.Where(z => z.x > z.y)
.Any();
如果我使用以下内容进行测试:
byte[] rv1 = new byte[] 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01 ;
byte[] rv2 = new byte[] 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x05 ;
Console.WriteLine(isGreater(rv1, rv2));
Console.WriteLine(isGreater(rv2, rv1));
...我得到了预期的结果:
错误的 真的【讨论】:
以上是关于如何在 C# 或 Linq 中比较大于或小于运算符值的两个字节数组?的主要内容,如果未能解决你的问题,请参考以下文章