32

I have a set of extension methods that I regularly use for various UI tasks. I typically define them to run off of type object, even though inside of them I'm typically converting them to string types.

public static string FormatSomething(this object o)
{
     if( o != null )
     {
          string s = o.ToString();
          /// do the work and return something.
     }
     // return something else or empty string.

}

The main reason I use type object and not string is to save myself in the UI from having to do <%#Eval("Phone").ToString().FormatSomething()%> when I can do <%#Eval("Phone").FormatSomething()%> instead.

So, is it fine from performance standpoint to create all the extension methods on object, or should I convert them to be string (or relevant) types based on what the extension method is doing?

StayOnTarget
  • 11,743
  • 10
  • 52
  • 81
Doozer Blake
  • 7,677
  • 2
  • 29
  • 40

4 Answers4

65

Is there a performance hit for creating extension methods that operate off the object type?

Yes. If you pass a value type in then the value type will be boxed. That creates a performance penalty of allocating the box and doing the copy, plus of course later having to garbage collect the box.

Instead of

public static string FormatSomething(this object o) 
{
    return (o != null) ? o.ToString() : "";
}

I would write

public static string FormatSomething<T>(this T o) 
{
    return (o != null) ? o.ToString() : "";
}

That has the same effect, but avoids the boxing penalty. Or rather, it trades a per call boxing penalty for a first call jitting cost penalty.

is it fine from performance standpoint to create all the extension methods on object?

We cannot answer the question. Try it! Measure the performance, compare that against the desired performance, and see if you met your goal. If you did, great. If not, use a profiler, find the slowest thing, and fix it.

But neither question is the question you should be asking. The question you should have asked is:

Is it a good programming practice to create an extension method that extends everything?

No. It is almost never a good idea. In most cases where people want to do that, they are abusing the extension method mechanism. Typically there is some more specific type that could be extended. If you do this a lot then you end up with lots of extension methods on every type, and coding becomes confusing and error-prone.

For example, suppose you want to have an extension method that answers the question "does this sequence contain this value?" You could write:

public static bool IsContainedIn<T>(this T item, IEnumerable<T> sequence)

and then say

if (myInt.IsContainedIn(myIntSequence))

But it is much better to say:

public static bool Contains<T>(this IEnumerable<T> sequence, T item)

and then say

if (myIntSequence.Contains(myInt))

If you do it the first way then you're typing along in the IDE and every single time you type ".", you get prompted with IsContainedIn as an option because maybe you're about to write code that determines if this object is in a collection. But 99% of the time, you're not going to do that. Doing this adds noise to the tooling and makes it harder to find what you really want.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • 3
    So, to be clear, the bottle-neck you're pointing out isn't specific to extension methods, but any method which passes a value-type by reference – STW Oct 04 '11 at 19:37
  • 4
    @STW: Yes. It is just particularly egregious with extension methods on object because the boxing is non-obvious. It looks like just a method on the value, which you would naively expect to not box anything. – Eric Lippert Oct 04 '11 at 19:42
  • There are several methods on valuetypes that box on calling. For all value types there is `GetType`, and things like `GetHashCode`,`Equals` and `ToString` if you don't implement them yourself. (Also all instance methods on `Enum` boxs) – Michael B Oct 04 '11 at 20:13
  • @MichaelB: Correct. Which is quite vexing, I agree. – Eric Lippert Oct 04 '11 at 20:23
  • @EricLippert isnt the `T` in `(this T o) ` is actually /eventually system.object ? – Royi Namir Apr 05 '12 at 06:53
  • @RoyiNamir: No. It is whatever type is supplied for the type argument. You're thinking like C# uses Java generics; C# generics, unlike Java generics, actually work like it says on the label: they are actual generic types, not fancy ways to say "object". – Eric Lippert Apr 05 '12 at 13:46
  • @EricLippert thank you. 1 thing that bothers me that if I told to a person `whatever type is supplied for the type argument` he will tell me : convince me that its **not system.object**...and i will have **no ** proof....:( – Royi Namir Apr 05 '12 at 13:57
  • @RoyiNamir: Well, what *evidence* would count as "proof"? – Eric Lippert Apr 05 '12 at 14:35
  • @RoyiNamir how about `item.GetType() != typeof(System.Object)`? – phoog Apr 05 '12 at 21:14
  • @phoog yeah ... there is something about it...but again ...all inherit from object..... – Royi Namir Apr 05 '12 at 21:15
  • @RoyiNamir ah yes, I see. You could call `Contains` and pass a list of strings and a string value to look for. But... what does it matter? – phoog Apr 05 '12 at 21:22
  • 1
    @phoog: Close. Calling GetType on a variable tells you nothing about the type *of the variable* except that the variable must be assignment compatible with the value that is in it. What you want to do instead is do `typeof(T) != typeof(object)` -- this gives you evidence that the type system considers T to be whatever type argument was given. But really the whole question of "proof" is an odd one. – Eric Lippert Apr 05 '12 at 22:14
  • @EricLippert - why is the question of proof odd? If you assert that C# generics are meaningfully different than Java's in this way, then it seems natural for someone to ask you for some evidence that this is the case. Of course, you are correct that `typeof(T) != typeof(object)` is one way to demonstrate it. – kvb Apr 05 '12 at 22:57
  • @EricLippert yes, I realized that as soon as Royi Namir replied to my comment -- hence my comment about calling `Contains` and passing strings. But thanks for mentioning `typeof(T) != typeof(object)`; I'm a bit frazzled today and should have thought of that myself :-) – phoog Apr 06 '12 at 03:56
  • Guys i just want a proof thats we are not extending system.Object....since T can actually except AnyThing ! – Royi Namir Apr 06 '12 at 08:53
  • @RoyiNamir: Like I said, I can't figure out what evidence would satisfy your need for "proof". When you say `M(this object x)` you are writing a method that *extends only object*. When you say `M(this T x)` you are writing a method that *extends every type that is convertible to object via an identity, reference or boxing conversion*. If you don't consider that distinction to be interesting or important, there's not much I can do to convince you otherwise. – Eric Lippert Apr 06 '12 at 13:48
  • @EricLippert (thanks for reply) But let me put it this way : can you give me an example which will work through `M(this T x)` and NOT in `M(this object x)` ? or vice verse ? – Royi Namir Apr 06 '12 at 13:51
  • @RoyiNamir: Think about it this way. I hand you a pile of tickets. Each one says "The person who is holding this ticket gets a free ice cream cone". I hand you a second pile of tickets. They say "The person named _________ who is holding this ticket gets a free ice cream cone." You can fill in anyone's name on that ticket. My claim is that those two piles of tickets are *different*. You want *proof*. What proof would satisfy you? – Eric Lippert Apr 06 '12 at 14:38
  • @Royi Namir: Do you understand that `static void M(T t)` and `static void M(object obj)` are different? One huge difference is that the former will not box parameters that have compile-time type that is a value type, the latter will. Now understand that the extension methods are **nothing** but syntactic sugar over these methods. That is, calls to `static void M(this T t)` resolve to calls to a method that looks like `static void M(T t)` and calls to `static void M(object obj)` resolve to calls to a method that looks like `static void M(object obj)`. – jason Apr 06 '12 at 14:50
  • @Royi Namir: If you understand the first point I made, that there is a difference between `static void M(T t)` and `static void M(object obj)`, and you understand that extension methods are just syntactic sugar over these methods, then you have to accept that `static void M(this T t)` and `static void M(this object obj)` are different too. – jason Apr 06 '12 at 15:00
  • @Jason - I think the question may be, suppose our nemesis comes up to us and (incorrectly) states that the runtime actually does implement generics by boxing arguments of generic types (and unboxing return values of generic types). What _observable behavior_ can we use to show him that he's incorrect? – kvb Apr 06 '12 at 16:16
  • @kvb: Are you telling me that showing him the IL that doesn't have any boxing calls for the former but does for the latter is not enough? – jason Apr 06 '12 at 16:17
  • @kvb: Consider this way of looking at the question: could a perverse implementation of the runtime deliberately and unnecessarily box the values *without introducing any observable difference to the program other than its bad performance*? If the answer is "yes" then there is not much you can say to your nemesis. – Eric Lippert Apr 06 '12 at 16:35
  • 1
    @kvb: One interesting avenue to explore is *stuff that cannot be boxed*. For example, if you have `M(ref T t)` and call `M` then T cannot secretly be object because *then you could assign a non-int to the aliased variable on another thread*, and thereby read a non-T out of a variable of type T. The variable aliased is required to be a bona-fide variable of exactly that type. – Eric Lippert Apr 06 '12 at 16:38
  • @Jason - Well, I'm not sure exactly what Royi was after, but sure, let's say that our nemesis is persistent and claims that the lack of a `box` opcode just means that the C# compiler isn't explicitly boxing the value. Why couldn't it still be the case that at runtime the CLR implements generic methods a single time and boxes their inputs as necessary, rather than re-jitting the method based on the actual generic arguments? I think that Eric's `typeof(T) == typeof(object)` test is a good argument against this. Is there more direct evidence that the arguments aren't really boxed? – kvb Apr 06 '12 at 16:43
  • @EricLippert Would you do us a favor and take this to chat? Comments are not the place to have extended conversations or store important information, they're meant to clarify the post, so please, if related to the post, then update the post and delete your comments, or continue this in chat, or another post. Thanks in advance. – casperOne Apr 06 '12 at 16:43
  • @kvb Would you do us a favor and take this to chat? Comments are not the place to have extended conversations or store important information, they're meant to clarify the post, so please, if related to the post, then update the post and delete your comments, or continue this in chat, or another post. Thanks in advance. – casperOne Apr 06 '12 at 16:43
  • @EricLippert - thanks, that's another good example. I figured that there might be a good example when the argument is a mutable struct, but couldn't think of anything convincing offhand. – kvb Apr 06 '12 at 16:44
2

I seriously doubt there would be any performance implications outside of perhaps some IDE impact. Once compiled I wouldn't expect it would make any difference.

STW
  • 44,917
  • 17
  • 105
  • 161
2

Compared to when you call ToString befor the call to FormatSomething not really (you're null check might take a few more ms but they also make the code more robust.

Even if the compile time type of the object you're calling the method on was string it still would make a visible difference.

Don't worry about performance until you have a performance issue. Until then worry about maintainability including readability. If you have a performance problem then use a profiler to find where it is.

Rune FS
  • 21,497
  • 7
  • 62
  • 96
1

What overhead is associated with an extension method at runtime? (.NET) answers your question I think. Extension methods are just static methods, so they do not belong on the Object type. Intellisense only makes it seem so.

Community
  • 1
  • 1
kmkemp
  • 1,656
  • 3
  • 18
  • 21