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?
Update
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.
CodePudding user response:
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;
}
CodePudding user response:
Since you said you needed indexed access for other methods of this kind, you can define a wrapper around string
for that and use an implicit typecasting operator for that wrapped class. This might be more expensive than what you're willing to save, but it's possible:
public interface IIndexedChar
{
char this[int index] { get; }
int Length { get; }
}
public struct IndexedString : IIndexedChar {
public string Value { get; }
public IndexedString(string value) => Value = value;
public this[int index] => Value[index];
public int Length => Value.Length;
static implicit operator IndexedString(string s) => new IndexedString(s);
}
// implement the same interface in your type too
and write your functions like this:
public static int IndexOf(this IIndexedChar s, Func<char, bool> predicate, int startIndex)
{
for (int i = startIndex; i < s.Length; i )
{
if (predicate(s[i]))
return i;
}
return -1;
}