A generic method with an argument of two possible types using generic methods

I am writing a generic method that will be used with either String or StringBuilder . It returns the position of the second word in the argument (words can be separated by spaces and line breaks). To use the arguments [] and Length() I couldn't think of anything better than the ugly code below. Is there a more elegant way to do this?

 int PositionOfTheSecondWord<T>(T text) // T can be String or StringBuilder { int pos = 0; int state = 0; char c; // Get length of the text // UGLY! int length = text is StringBuilder ? (text as StringBuilder).Length : (text as String).Length; while (pos <= length - 1) { // Get the next character // UGLY! c = text is StringBuilder ? (text as StringBuilder)[pos] : (text as String)[pos]; if (c == ' ' || c == '\n') // space { if (state == 1) state = 2; // 2 means the space between the first and the second word has begun } else // a letter if (state == 0) state = 1; // 1 means the first word has begun if (state == 2) return pos; pos++; } return -1; } 

P. S. I can't just write a function for the String argument and call it from StringBuilder.ToString (), because my StringBuilder can be huge.

+4
source share
4 answers

Depending on the length of your method, this may be the situation for polymorphism:

As noted in other answers, string and StringBuilder not related to each other. Therefore, your only chance to use the same method for them is to create wrappers for these two types that are interconnected.

You can define the base wrapper class using your method as follows:

 public abstract class ScannableStringBase { public abstract int Length { get; } public abstract char this[int index] { get; } public int PositionOfTheSecondWord() { int pos = 0; int state = 0; char c; int length = this.Length; while (pos <= length - 1) { c = this[pos]; if (c == ' ' || c == '\n') // space { if (state == 1) state = 2; // 2 means the space between the first and the second word has begun } else // a letter if (state == 0) state = 1; // 1 means the first word has begun if (state == 2) return pos; pos++; } return -1; } } 

From this class, derive subclasses that handle the required value types:

 public class ScannableString : ScannableStringBase { public ScannableString(string value) { this.stringValue = value; } private readonly string stringValue; public override int Length { get { return stringValue.Length; } } public override char this[int index] { get { return stringValue[index]; } } } public class ScannableStringBuilder : ScannableStringBase { public ScannableString(stringBuilder value) { this.stringBuilder = value; } private readonly string stringBuilder; public override int Length { get { return stringBuilder.Length; } } public override char this[int index] { get { return stringBuilder[index]; } } } 

To summarize, you get:

  • There is no code duplication because PositionOfTheSecondWord() is defined only once, in the base class.
  • Type is security because your PositionOfTheSecondWord() method cannot be called by anything other than string or StringBuilder .
  • Extensibility, because if you ever find that you want to support the third type, you can simply derive another class from ScannableStringBase .

One possible flaw may be that you must distinguish the type for analysis somewhere in advance, so you can decide whether to create instances of ScannableString or ScannableStringBuilder .

+1
source

I think the best approach is to use overloaded methods. For instance:.

 int PostionOfTheSecondWord(string text) { // Code optimized for strings. } int PostionOfTheSecondWord(StringBuilder text) { // Code optimized for StringBuilder. } 

This will make your code much easier to read and much easier to maintain, and it will work much better.

Hope this helps you in your quest.

+4
source
 int GetPos(string text) { int length = text.Length; for (int i = 0; i < length; i++) { if (GetChar(text, i) == ' ') { return i; } } return -1; } int GetPos(StringBuilder sb) { int length = sb.Length; for (int i = 0; i < length; i++) { if (GetChar(sb, i) == ' ') { return i; } } return -1; } char GetChar<T>(T text, int pos) { if (text.GetType() == typeof(StringBuilder)) { return (text as StringBuilder)[pos]; } else if (text.GetType() == typeof(String)) { return (text as String)[pos]; } else { throw new ArgumentException("Wrong parameter, T must be string or StringBuilder"); } } 
0
source

If you want to use the general method, you can use this simplest code with the same effect.

 // T can be String or StringBuilder: static int PositionOfTheSecondWordNew<T>(T text) { int pos = -1; string[] word; // If T is string or StringBuilder this line is not necessary: if ((text is StringBuilder) || (text is string)) { word = text.ToString().Split(new char[]{' ', '\n'}); pos = word[0].Length; } return pos; } 
0
source

Source: https://habr.com/ru/post/1434439/


All Articles