英文:
Which solution is the closes to type-specific implementation of generic class?
问题
C++有一个功能我想在C#中使用,即为泛型类实现特定类型的实现。我的意思是,我可以定义一个泛型类,但为特定泛型类型提供一个例外。
我正在编写一个文本比较工具。我已经实现了按行比较两个文件,现在我想按字符比较它们。算法完全相同,只是比较的类型不同。在第一种情况下,我正在对文本行进行安全哈希处理,将它们转换为int
(在这里的安全哈希处理指的是不同的行总是具有不同的哈希值),在第二种情况下,我想使用char
。
最简单的解决方案是将字符转换为整数,但在最坏的情况下,会使需要保存所有条目的内存量增加四倍。我可以简单地保存对象,但对它们应用==运算符可能会导致装箱,这将导致性能显著下降。因此,我转向了泛型。
public class ComparisonContext<TData> where TData : struct
{
public TData[] DataA { get; }
public TData[] DataB { get; }
}
// (...)
if (context.DataA[x].Equals(context.DataB[y])) ...
不过,我不确定这是否仍然会导致装箱。我考虑过的一个解决方案是要求TData
实现IEquatable<TData>
。
public class ComparisonContext<TData> where TData : struct, IEquatable<TData>
{
public TData[] DataA { get; }
public TData[] DataB { get; }
}
// (...)
if (((IEquatable<TData>)context.DataA[x]).Equals(context.DataB[y])) ...
最佳解决方案只是在上下文类中实现一个特定于char
和int
类型的比较方法。但在C#中,这是不可能的。
在性能和可读性方面,是否有比我想出的更好的解决方案?
英文:
C++ has a feature I'd like to use in C#, namely type-specific implementation for generic class. What I mean is that I can define a generic class, but provide an exception for specific generic type.
I'm writing a text comparison tool. I've already implemented comparing two files by lines and now I want to implement comparing them by characters. The algorithm is exactly the same - except the compared type. In the first case I'm doing a safe-hashing of text lines into int
s (safe-hashing in terms that different lines always have different hashes), in the second case I'd like to use char
s.
The naïve solution is to cast chars to ints, but in the worst case it will quadruple amount of memory needed to keep all entries. I could simply hold objects, but applying == to them might cause boxing, which will result in significant performance drops. So I turned to generics.
public class ComparisonContext<TData> where TData : struct
{
public TData[] DataA { get; }
public TData[] DataB { get; }
}
// (...)
if (context.DataA[x].Equals(context.DataB[y])) ...
I'm unsure though if it still won't cause boxing. One solution I thought about is requiring that TData
implements IEquatable<TData>
.
public class ComparisonContext<TData> where TData : struct, IEquatable<TData>
{
public TData[] DataA { get; }
public TData[] DataB { get; }
}
// (...)
if (((IEquatable<TData>)context.DataA[x]).Equals(context.DataB[y])) ...
The best solution would be simply to implement a comparison method inside the context class, specific to char
and int
types. But in C# this is not possible.
Is there any better solution in terms of performance and readability than what I came up with?
答案1
得分: 2
以下是翻译好的内容:
在上下文类内部实现一个比较方法,专门针对
char
和int
类型。但在 C# 中,这是不可能的。
如果我理解正确,您只希望在 ComparisonContext<int>
和 ComparisonContext<char>
上使用扩展方法?
public static class Extensions {
public static bool DataAreEqual(this ComparisonContext<int> context, int dataAIndex, int dataBIndex)
=> context.DataA[dataAIndex] == context.DataB[dataBIndex];
public static bool DataAreEqual(this ComparisonContext<char> context, int dataAIndex, int dataBIndex)
=> context.DataA[dataAIndex] == context.DataB[dataBIndex];
}
用法:
context.DataAreEqual(x, y);
// 而不是
// ((IEquatable<TData>)context.DataA[x]).Equals(context.DataB[y])
在您的第一种方法中确实会发生装箱,如果您正在使用的 context
是 ComparisonContext<TData>
。由于 TData
仅限于值类型,因此在其上调用 Equals
将调用 Equals(object)
。如果 context
实际上是 ComparisonContext<int>
,其中类型参数是具有自己的接受自己类型的 Equals
的类型,那么就不会发生装箱。
在您的第二种方法中也会发生装箱,但这只是因为您进行了到 IEquatable<T>
的强制转换,这是不必要的。
如果您编写了 where TData : struct, IEquatable<TData>
并且只是这样写:
context.DataA[x].Equals(context.DataB[y])
如果您使用的是 .NET 7+,您还可以将 TData
限制为 IEqualityOperators<TData, TData, bool>
。然后,您可以直接使用 ==
进行比较,我认为这样更可读。这也不会装箱。
英文:
> to implement a comparison method inside the context class, specific to char
and int
types. But in C# this is not possible.
If I understand correctly, you just want extension methods on ComparisonContext<int>
and ComparisonContext<char>
?
public static class Extensions {
public static bool DataAreEqual(this ComparisonContext<int> context, int dataAIndex, int dataBIndex)
=> context.DataA[dataAIndex] == context.DataB[dataBIndex];
public static bool DataAreEqual(this ComparisonContext<char> context, int dataAIndex, int dataBIndex)
=> context.DataA[dataAIndex] == context.DataB[dataBIndex];
}
Usage:
context.DataAreEqual(x, y);
// instead of
// ((IEquatable<TData>)context.DataA[x]).Equals(context.DataB[y])
<hr>
Boxing does indeed occur in your first approach, if the context
that you are using is a ComparisonContext<TData>
. Since TData
is only constrained to value types, so calling Equals
on that will call Equals(object)
. If context
is actually a ComparisonContext<int>
, where the type argument is a type that has their own Equals
that takes their own type, then there is no boxing.
Boxing also occurs in your second approach, but that's only because you casted to IEquatable<T>
, which is unnecessary.
No boxing occurs if you wrote where TData : struct, IEquatable<TData>
and just did:
context.DataA[x].Equals(context.DataB[y])
If you are on .NET 7+, you can also constrain TData
to IEqualityOperators<TData, TData, bool>
.Then you can just compare them directly with ==
, which I think is a bit more readable. This also does not box.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论