Is there a performance hit for creating Extension methods that operate off the type 'object'?
Asked Answered
B

4

35

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?

Bourgeoisie answered 4/10, 2011 at 18:2 Comment(2)
The real question is does it matter?Amphithecium
@RuneFS Oh, it really doesn't, and I'm guessing any performance hit would probably be negligible. Mostly curiosity.Bourgeoisie
K
67

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.

Kramlich answered 4/10, 2011 at 18:24 Comment(28)
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 referencePolemics
@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.Kramlich
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)Niagara
@MichaelB: Correct. Which is quite vexing, I agree.Kramlich
@EricLippert isnt the T in <T>(this T o) is actually /eventually system.object ?Flourishing
@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".Kramlich
@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....:(Flourishing
@RoyiNamir: Well, what evidence would count as "proof"?Kramlich
@RoyiNamir how about item.GetType() != typeof(System.Object)?Fley
@Fley yeah ... there is something about it...but again ...all inherit from object.....Flourishing
@RoyiNamir ah yes, I see. You could call Contains<object> and pass a list of strings and a string value to look for. But... what does it matter?Fley
@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.Kramlich
@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.Shamanism
@EricLippert yes, I realized that as soon as Royi Namir replied to my comment -- hence my comment about calling Contains<object> and passing strings. But thanks for mentioning typeof(T) != typeof(object); I'm a bit frazzled today and should have thought of that myself :-)Fley
Guys i just want a proof thats we are not extending system.Object....since T can actually except AnyThing !Flourishing
@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<T>(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.Kramlich
@EricLippert (thanks for reply) But let me put it this way : can you give me an example which will work through M<T>(this T x) and NOT in M(this object x) ? or vice verse ?Flourishing
@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?Kramlich
@Royi Namir: Do you understand that static void M<T>(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<T>(this T t) resolve to calls to a method that looks like static void M<T>(T t) and calls to static void M(object obj) resolve to calls to a method that looks like static void M(object obj).Christly
@Royi Namir: If you understand the first point I made, that there is a difference between static void M<T>(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<T>(this T t) and static void M(this object obj) are different too.Christly
@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?Shamanism
@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?Christly
@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.Kramlich
@kvb: One interesting avenue to explore is stuff that cannot be boxed. For example, if you have M<T>(ref T t) and call M<int> 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.Kramlich
@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?Shamanism
@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.Coil
@Shamanism 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.Coil
@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.Shamanism
P
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.

Polemics answered 4/10, 2011 at 18:3 Comment(0)
A
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.

Amphithecium answered 4/10, 2011 at 18:18 Comment(0)
L
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.

Lobe answered 4/10, 2011 at 18:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.