Other ways to deal with “loop initialization” in C#

前端 未结 9 724
陌清茗
陌清茗 2020-12-24 07:22

To start with I\'ll say that I agree that goto statements are largely made irrelevant by higher level constructs in modern programming languages and shouldn\'t be used when

相关标签:
9条回答
  • 2020-12-24 08:08

    You can certainly create a goto solution in C# (note: I didn't add null checks):

    string Join(string[] array, string delimiter) {
      var sb = new StringBuilder();
      var enumerator = array.GetEnumerator();
      if (enumerator.MoveNext()) {
        goto start;
        loop:
          sb.Append(delimiter);
          start: sb.Append(enumerator.Current);
          if (enumerator.MoveNext()) goto loop;
      }
      return sb.ToString();
    }
    

    For your specific example, this looks pretty straighforward to me (and it's one of the solutions you described):

    string Join(string[] array, string delimiter) {
      var sb = new StringBuilder();
      foreach (string element in array) {
        sb.Append(element);
        sb.Append(delimiter);
      }
      if (sb.Length >= delimiter.Length) sb.Length -= delimiter.Length;
      return sb.ToString();
    }
    

    If you want to get functional, you can try using this folding approach:

    string Join(string[] array, string delimiter) {
      return array.Aggregate((left, right) => left + delimiter + right);
    }
    

    Although it reads really nice, it's not using a StringBuilder, so you might want to abuse Aggregate a little to use it:

    string Join(string[] array, string delimiter) {
      var sb = new StringBuilder();
      array.Aggregate((left, right) => {
        sb.Append(left).Append(delimiter).Append(right);
        return "";
      });
      return sb.ToString();
    }
    

    Or you can use this (borrowing the idea from other answers here):

    string Join(string[] array, string delimiter) {
      return array.
        Skip(1).
        Aggregate(new StringBuilder(array.FirstOrDefault()),
          (acc, s) => acc.Append(delimiter).Append(s)).
        ToString();
    }
    
    0 讨论(0)
  • 2020-12-24 08:11

    There are ways you "can" get around the doubled code, but in most cases the duplicated code is much less ugly/dangerous than the possible solutions. The "goto" solution you quote doesn't seem like an improvement to me - I don't really think you really gain anything significant (compactness, readability or efficiency) by using it, while you increase the risk of a programmer getting something wrong at some point in the code's lifetime.

    In general I tend to go for the approach:

    • A special case for the first (or last) action
    • loop for the other actions.

    This removes the inefficiencies introduced by checking if the loop is in the first iteration on every time around, and is really easy to understand. For non-trivial cases, using a delegate or helper method to apply the action can minimise code duplication.

    Or another approach I use sometimes where efficiency isn't important:

    • loop, and test if the string is empty to determine if a delimiter is required.

    This can be written to be more compact and readable than the goto approach, and doesn't require any extra variables/storage/tests to detect the "special case" iteraiton.

    But I think Mark Byers' approach is a good clean solution for your particular example.

    0 讨论(0)
  • 2020-12-24 08:12

    If you want to go the functional route, you could define String.Join like LINQ construct that is reusable across types.

    Personally, I would almost always go for code clarity over saving a few opcode executions.

    EG:

    namespace Play
    {
        public static class LinqExtensions {
            public static U JoinElements<T, U>(this IEnumerable<T> list, Func<T, U> initializer, Func<U, T, U> joiner)
            {
                U joined = default(U);
                bool first = true;
                foreach (var item in list)
                {
                    if (first)
                    {
                        joined = initializer(item);
                        first = false;
                    }
                    else
                    {
                        joined = joiner(joined, item);
                    }
                }
                return joined;
            }
        }
    
        class Program
        {
    
            static void Main(string[] args)
            {
                List<int> nums = new List<int>() { 1, 2, 3 };
                var sum = nums.JoinElements(a => a, (a, b) => a + b);
                Console.WriteLine(sum); // outputs 6
    
                List<string> words = new List<string>() { "a", "b", "c" };
                var buffer = words.JoinElements(
                    a => new StringBuilder(a), 
                    (a, b) => a.Append(",").Append(b)
                    );
    
                Console.WriteLine(buffer); // outputs "a,b,c"
    
                Console.ReadKey();
            }
    
        }
    }
    
    0 讨论(0)
提交回复
热议问题