Reputation: 67223
I have the following two extension methods. (I have a lot more, but I'll use these for this discussion.)
public static MyExtensions
{
public static int IndexOf(this string s, Func<char, bool> predicate, int startIndex)
{
for (int i = startIndex; i < s.Length; i++)
{
if (predicate(s[i]))
return i;
}
return -1;
}
public static int IndexOf(this StringEditor s, Func<char, bool> predicate, int startIndex)
{
for (int i = startIndex; i < s.Length; i++)
{
if (predicate(s[i]))
return i;
}
return -1;
}
}
They both do the same thing. One works with type string
. And the other works with type StringEditor
, which is a class I created.
My question is if anyone can think of a way to implement both in a single method using generics.
I cannot modify string
, but I can make any changes needed to StringEditor
.
I cannot derive StringEditor
from string
because string
is sealed.
I couldn't find any interface implemented by string
for accessing individual characters that I could implement in StringEditor
. IEnumerable<T>
is available but does not support accessing individual characters directly like an array.
And it isn't valid to make a type argument for both string
and StringEditor
.
public static int IndexOf<T>(this T s, Func<char, bool> predicate, int startIndex) where T : string, StringEditor
I don't think this can be done. But maybe someone is more clever than me?
Where I described that I needed to directly access individual characters, I meant using the s[i]
syntax. In addition to generally being less performant, IEnumerable<T>
does not support this.
While IEnumerable<T>
could be used to perform the results of my two methods here, it does not allow direct character access.
Upvotes: 0
Views: 93
Reputation: 17248
You could use ReadOnlySpan<char>
as argument type.
public static int IndexOf(this ReadOnlySpan<char> s, Func<char, bool> predicate, int startIndex)
ReadOnlySpan<T>
supports indexing. Since there's an implicit conversion from string to ReadOnlySpan<char>
you don't even need to modify the call sites. And there's no measurable performance penalty with this conversion, since creating a Span is basically free, both in time as well as in memory usage.
Upvotes: 1
Reputation: 52250
Generics not needed. Use IEnumerable<char>
, which is implemented by String
and could be implemented by your StringEditor
.
public static int IndexOf(this IEnumerable<char> s, Func<char, bool> predicate, int startIndex)
{
int i = startIndex;
foreach (char c in s.Skip(startIndex))
{
if (predicate(c))
return i;
i++;
}
return -1;
}
Or
public static int IndexOf(this IEnumerable<char> source, Func<char, bool> predicate, int startIndex)
{
var s = source.ToArray();
for (int i = startIndex; i < s.Length; i++)
{
if (predicate(s[i]))
return i;
}
return -1;
}
Upvotes: 1