In short: is it possible to define a generic method where the type parameter (T
) is constrained to string
or int[]
? In pseudo-C#, what I want to do is:
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...
}
Note that in C#, due to the built-in string
indexer (which returns a char
) along with implicit conversion from char
to int
, the following expression means precisely the same thing whether source
is a string
or an int[]
:
int someval = source[index];
Per the thread Constrain generic extension method to base types and string I realize I can't just make a list of unassociated types in the where T : x...
constraint clause. Both int[]
and string
would comply with T: IEnumerable<int>
, but IEnumerable<T>
does not require implementers to have an indexer, which is exactly the common feature I am using from both types.
The purpose behind this is that I'm building some highly optimized string parsing and analyzing functions such as a fast implementation of the Damerau–Levenshtein distance algorithm. I've found that first converting my strings to arrays of int can sometimes yield significantly faster executions in repetitive character-by-character processing (as with the D-L algorithm). This is largely due to the fact that comparing int
values is much faster that comparing char
values.
The operative word is 'sometimes'. Sometimes it's faster to operate directly on the strings and avoid the cost of first converting and copying to arrays of int
. So I now have methods that are truly identical except for the declarations.
Of course I can use dynamic
, but the performance penalty from runtime checking completely destroys any gains made in the construction of the methods. (I did test it).
An attribute cannot inherit from a generic class, nor can a generic class inherit from an attribute.
A type constraint on a generic type parameter indicates a requirement that a type must fulfill in order to be accepted as a type argument for that type parameter. (For example, it might have to be a given class type or a subtype of that class type, or it might have to implement a given interface.)
The where clause in a generic definition specifies constraints on the types that are used as arguments for type parameters in a generic type, method, delegate, or local function.
Multiple interface constraints can be specified. The constraining interface can also be generic.
You cannot have a constraint that says “the type must have an indexer”.
However, you can have a constraint that says “the type must implement an interface that has an indexer”. Such an interface might be IList<char>
, for example.
Unfortunately, string
doesn’t implement IList<char>
, so you would have to write a small wrapper class for it:
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)
}
And then you can declare your method like this:
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
No, C# does not allow you to create a "composite" constraint like that.
The expression you show does mean the same thing, but you can't use that fact to your advantage. C# generics resemble C++ templates syntactically, but they work completely differently, so the fact that the indexer does the same thing ends up being irrelevant.
You could, of course, just create the two overloads with the specific types you need, but that does mean some annoying copy & paste. Any attempts at abstracting this to avoid repetition will cost you performance badly.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With