Kevin
Kevin

Reputation: 13

Write IEnumerable extension methods to join a collection of strings

I'm pretty new to C#, and I'm trying to write an extension method to join a collection of objects/strings with custom quote marks. I have 2 ways of implement it, trying to figure out which way is the standard approach.

1st method, define one method for each type I need:

public static string JoinToString(this IEnumerable<string> stringEnumerable, string delimiter = ",", char quote = '\'');
{
    return string.Join(delimiter, stringEnumerable.Select(item => quote + item + quote));
}
public static string JoinToString(this IEnumerable<int> intEnumerable, string delimiter = ",", char quote = '\'')
{
    return string.Join(delimiter, intEnumerable.Select(item => quote + item.ToString() + quote));
}

etc...


2nd method: define only object parameter so it can work with any type (just like Console.WriteLine(object))

public static string JoinToString(this IEnumerable<object> enumerable, string delimiter = ",", char quote = '\'')
{
    return string.Join(delimiter, enumerable.Select(item => quote + item.ToString() + quote));
}

which one is the commonly acceptable / standard approach? Thanks!

Upvotes: 1

Views: 491

Answers (3)

Dmitrii Bychenko
Dmitrii Bychenko

Reputation: 186738

There are several issues with your code:

  1. Since you implement public method you should validate input parameters (enumerable, delimiter)
  2. You should check if item contains either delimiter or quote.
  3. If item contains delimiter or / and quote you, probably, have to escape them
  4. You have to check item for null
  5. We usually extend generic IEnumerable<T>, not IEnumerable<object>

Something like this:

public static string JoinToString<T>(this IEnumerable<T> source,
                                          string delimiter = ",",
                                          char quote = '\'',
                                          char escape = '\'') {

  string Escape(string value) {
    if (escape == '\0') // in case we don't want to escape at all
      return value;

    StringBuilder sb = new StringBuilder(value.Length * 2);

    foreach (var c in value) {
      if (c == quote || c == escape)
        sb.Append(escape);

      sb.Append(c);
    }

    return sb.ToString(); 
  }

  if (source is null)
    throw new ArgumentNullException(nameof(source));
  if (delimiter is null)
    throw new ArgumentNullException(nameof(delimiter));

  return string.Join(delimiter, source
    .Select(item => item?.ToString() ?? "")
    .Select(item => $"{quote}{Escape(item)}{quote}"));
}

Upvotes: 0

klekmek
klekmek

Reputation: 563

I would go for the strongly typed version (1st). The issue with creating an extension method on this IEnumerable<object> is that you will see this extension method on ALL lists as every class inherits of object.

In a small codebase this would not be an issue, however I have seen codebases where your IDE and IntelliSense has too many options for irrelevant extension methods.

Keep it simple and small and solve on the issue at hand.

Upvotes: 0

MindSwipe
MindSwipe

Reputation: 7910

The common/ standard approach when working with Linq/ extension methods is to use generics unless you have a reason not to. So

public static string JoinToString<T>(this IEnumerable<T> source, string delimiter = ",", char quote = '\'')
{
    return string.Join(delimiter, source.Select(item => $"{quote}{item}{quote}"));
}

For future reference, you can also apply constraints on the generic type parameter if you want to restrict it so only a certain set of types can be passed to the generic method, which is handy when you want to access properties or methods on the object

Upvotes: 5

Related Questions