简体   繁体   中英

Is it possible to constrain a generic type parameter to String OR Array

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 DL 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).

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.

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

You could make your method signature

public static int MyMethod<T> ( T[] arg1, T[] arg2 )

and use String.ToCharArray() before passing string arguments (or maybe in an overload, you get the idea...)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM