是否可以将泛型类型参数约束为字符串或数组
本文关键字:字符串 数组 参数约束 泛型类型 是否 | 更新日期: 2023-09-27 18:20:26
简而言之:是否可以定义类型参数(T
)约束为string
或int[]
的泛型方法?在伪C#中,我想做的是:
public static int MyMethod<T> ( T arg1, T arg2 )
where T : (has an indexer that returns an int) {
// stuff with arg1[i], arg2[j], etc...
}
注意,在C#中,由于内置的string
索引器(返回char
)以及从char
到int
的隐式转换,无论source
是string
还是int[]
,以下表达式的含义都完全相同:
int someval = source[index];
根据线程Constrain泛型扩展方法到基类型和字符串,我意识到我不能在where T : x...
constraint子句中只列出未关联的类型。int[]
和string
都符合T: IEnumerable<int>
,但IEnumerable<T>
不要求实现者具有索引器,这正是我在这两种类型中使用的共同特性。
这背后的目的是,我正在构建一些高度优化的字符串解析和分析功能,例如Damerau–Levenstein距离算法的快速实现。我发现,在重复的逐字符处理中,首先将字符串转换为int数组有时可以更快地执行(就像D-L算法一样)。这在很大程度上是由于比较int
值比比较char
值快得多。
关键词是"有时"。有时,直接对字符串进行操作会更快,并避免首先转换和复制到int
数组的成本。因此,除了声明之外,我现在有了真正相同的方法。
当然,我可以使用dynamic
,但运行时检查带来的性能损失完全破坏了在构建方法时获得的任何收益。(我做了测试)。
不能有一个约束来表示"类型必须有一个索引器"。
但是,您可以有一个约束,它表示"类型必须实现一个具有索引器的接口"。例如,这样的接口可以是IList<char>
。
不幸的是,string
没有实现IList<char>
,所以您必须为它编写一个小的包装类:
sealed class StringWrapper : IList<char>
{
public string String { get; private set; }
public StringWrapper(string str) { String = str; }
public static implicit operator StringWrapper(string str)
{
return new StringWrapper(str);
}
public char this[int index]
{
get { return String[index]; }
set { throw new NotSupportedException(); }
}
// Etc.... need to implement all the IList<char> methods
// (just throw NotSupportedException except in the ones that are trivial)
}
然后你可以这样声明你的方法:
public static TElement MyMethod<TCollection, TElement>(TCollection arg)
where TCollection : IList<TElement>
{
return arg[0];
}
[...]
MyMethod<StringWrapper, char>("abc") // returns 'a'
MyMethod<int[], int>(new[] { 1, 2, 3 }) // returns 1
不,C#不允许创建这样的"复合"约束。
你表达的意思是一样的,但你不能利用这个事实为自己谋利。C#泛型在语法上类似于C++模板,但它们的工作方式完全不同,因此索引器做同样事情的事实最终是无关紧要的。
当然,您可以创建两个具有所需特定类型的重载,但这确实意味着一些烦人的复制&粘贴任何为了避免重复而对其进行抽象的尝试都会严重降低性能。
您可以将方法签名设置为
public static int MyMethod<T> ( T[] arg1, T[] arg2 )
并在传递字符串参数之前使用String.ToCharArray()(或者可能在重载中,你会想到…)