6

With the addtion of Extension methods to C# we've seen a lot of them crop up in our group. One debate revolves around extension methods like this one:

public static class StringExt
{
    /// <summary>
    /// Shortcut for string.Format.
    /// </summary>
    /// <param name="str"></param>
    /// <param name="args"></param>
    /// <returns></returns>
    public static string Format(this string str, params object[] args)
    {
        if (str == null) return null;
        return string.Format(str, args);
    }
}

Does this extension method break any programming best practices that you can name? Would you use it anyway, if not why? If I renamed the function to "F" but left the xml comments would that be epic fail or just a wonderful savings of keystrokes?

ChrisF
  • 38,878
  • 11
  • 125
  • 168
P. Roe
  • 593
  • 3
  • 12
  • 3
    What are you trying to achieve? If it's just keystrokes then use the built in auto complete or get something like ReSharper. – ChrisF Jun 22 '11 at 21:14
  • I wish, we've been beating the Resharper drum for over a year to deaf ears. In general though this is just an example of some of the petty useses of extension methods that are causing "best practice" debates. – P. Roe Jun 22 '11 at 21:18

4 Answers4

14

It seems pretty pointless to me.

It's more code that you have to maintain - and in a year's time no one will remember the debates you had and half your code will use string.Format the other half will use this method.

It's also distracting you from the real task of developing software that solves real problems.

You've probably wasted more money by having these discussions that it would have cost the company to buy ReSharper licenses for everyone.

ChrisF
  • 38,878
  • 11
  • 125
  • 168
  • Couldn't agree more on these "helper" methods and discussions being a waste of resources. Unfortunately, I think that Stack Overflow has [contributed significantly](http://stackoverflow.com/q/271398/38360) to the [problem](http://stackoverflow.com/q/954198/38360). I'll never understand why so many developers are so obsessed with saving a few keystrokes. *(Edit: I just noticed that this very method is one of the top-voted answers in the first link. {facepalm})* – Aaronaught Jun 23 '11 at 03:09
  • What's ReSharper, and what does it do - why do you recommend it for this particular problem? – blueberryfields Jun 24 '11 at 00:18
  • 2
    @blueberryfields ReSharper is a wonderful extension to visual studio that makes the cadillac of development environments more luxurious. One thing it does markably improve upon is the intelligence of intellisense, a second would be it takes code snippets to a new level. Learn more at http://www.jetbrains.com/resharper/ – Wyatt Barnett Nov 18 '13 at 18:22
6

FxCop complains when using String.Format(...) without an IFormatProvider. So it makes sense to have extension methods like or FormatInvariant(...) or FormatLocal(...) because it saves callers from being forced to fiddle around with the System.Globalization namespace a lot of times.

herzmeister
  • 429
  • 2
  • 6
  • Which of course this code does not accomplish as it would still complain about the extension method... – Ed S. Jun 23 '11 at 00:34
  • 1
    Sorry for not being clear enough. For example, the `FormatInvariant(...)` extension method would call `String.Format(CultureInfo.InvariantCulture, myString, ...)`. That will satisfy FxCop. – herzmeister Jun 23 '11 at 00:38
  • Of course, I just meant the OP's code, which is... useless. – Ed S. Jun 23 '11 at 00:40
  • 1
    I guess I just wanted to make the OP's proposal more useful and help him to find a good reason for such an extension method. :-> – herzmeister Jun 23 '11 at 00:52
  • 1
    Well, +1 for that then =D – Ed S. Jun 23 '11 at 00:52
2

Extension methods have one major disadvantage. Say you have an extension method like this:

public static string ToRomanNumeral(this int number)
{
    // whatever whatever whatever
}

You do this in .NET 4. Then in in .NET 5 Microsoft responds to the outpouring of Roman-numeral love among developers by adding a method to int:

public string ToRomanNumeral()
{
    // whatever whatever whatever
}

When you recompile your code against .NET 5, suddenly, and without any error or warning cropping up, Microsoft's Roman numeral code will be run instead of yours.

This is a silly example off the top of my head, but it can happen in any case where you use an extension method, then later add that same method to the class the extension method took as a parameter. In case of a conflict, methods on a class always take precedence over extension methods, and there's absolutely no notification of the conflict.

So...just something to keep in mind before you go extension-method crazy.

Kyralessa
  • 3,703
  • 2
  • 19
  • 20
  • Really there will be no warning? – Gulshan Jun 23 '11 at 10:26
  • Yep, really. Presumably because it would generate too many warnings, given things like the extension methods in System.Linq. – Kyralessa Jun 23 '11 at 11:58
  • @Kyralessa It would give a warning. If developers cannot keep on top of their warnings then that is another issue. Incidentally, it would not be a crazy idea to extend Char for a ToInt16(this Char ...) extension method providing that the conversation code was written safely (to return 0 if it was not a Roman numeral, remembering that there is no 0 in the Roman calculation system). – Phil C Dec 27 '11 at 16:03
  • @Carnotaurus, perhaps you mean to say that you feel it *should* give a warning. It *doesn't*, however. This is easy to test. – Kyralessa Dec 27 '11 at 18:20
  • 1
    @Kyralessa Interesting - I was using a prefix to all my extension methods which made each signature unique. When I added a new one "ToString", it didn't give a compilation warning, even though it bypassed my intended method. It looks like ... errmmm... you're right. – Phil C Dec 27 '11 at 23:43
1

I really dislike the fact that String.Format uses the current culture. This means that the output depends on the user's system. For example, if I code this quickly without thinking about cultures:

var now = DateTime.Now;
string json = String.Format("{{ 'Date': \"{0}\" }}", now);

I can test this, and it works great on my system.

{ 'Date': "9-11-2013 13:37:00" }

And after a while I forget about this little snippet of code, and it ends up in my product. Then some user in Saudi Arabia uses program, and everything fails since it produced:

{ 'Date': "06/01/35 01:37:00 ?" }

To prevent such bugs I want String.Format to use the invariant culture by default, and if I want to use a specific culture I can specify the culture explicitly. This will prevent all such bugs.


In C# 6 you can use string interpolation to do string formatting. It's still current culture specific though.

string json = $"{{ 'Date': \"{now}\" }}";

Luckily there is a way to make it culture invariant. Statically import the System.FormattableString namespace and use the Invariant method, like this:

using static System.FormattableString;

string json = Invariant($"{{ 'Date': \"{now}\" }}");

For C# 5 and earlier, I wrote myself a set of extension methods to do that for me. In the process, it also provides a much nicer syntax.

public static string FormatInvariant(this string format, params object[] args)
{
    if (format == null) throw new NullReferenceException("format");
    if (args == null) throw new NullReferenceException("args");

    return FormatWith(format, CultureInfo.InvariantCulture, args);
}

public static string FormatCurrentCulture(this string format, params object[] args)
{
    if (format == null) throw new NullReferenceException("format");
    if (args == null) throw new NullReferenceException("args");

    return FormatWith(format, CultureInfo.CurrentCulture, args);
}

public static string FormatWith(this string format, IFormatProvider provider, params object[] args)
{
    if (format == null) throw new NullReferenceException("format");
    if (provider == null) throw new NullReferenceException("provider");
    if (args == null) throw new NullReferenceException("args");

    return String.Format(provider, format, args);
}

(Get the full source code with comments and Code Contracts.)

Example usage:

string json = "{{ 'Date': \"{0}\" }}".FormatInvariant(now);
Daniel A.A. Pelsmaeker
  • 2,715
  • 3
  • 22
  • 27