Re: Avoiding Default Parameter Checking in C# 4.0

Tech-Archive recommends: Repair Windows Errors & Optimize Windows Performance



jehugaleahsa@xxxxxxxxx wrote:
I can call a public method in one class from another. The policy is/
was specific to in-class method calls.

I think we all understood that much.

I typically reserve public
methods for argument checking, exception handling and resource
management.

So, none of your in-class code gets these benefits? Seems like that would only increase overhead elsewhere, either in maintenance or duplicated code.

Then they just call a private method. The private method
stays really pure and simple, which makes it extremely readable.
Occasionally an exception needs thrown in the middle of the private
implementation, but this is a rare case.

The original reason, in the case of my Compass library, was because I
didn't want to hurt performance performing unnecessary argument
checks.

I find this comment ironic. It seems to me that the rule to never call public methods from within the class leads to extra method calls, itself a potential "performance hurting" operation. Performance-wise, it seems that if one has to choose between checking for a null pointer and making two method calls where one would suffice, the former would be preferable. (Granted, that may not be the specific pair of choices one is presented with...the example is just for the point of comparison).

For instance, I have a ToIList method which converts an
IEnumerable<T> to an IList<T> (using "as" to see whether it is
already one first). I do a check to see whether the IEnumerable<T> is
null and whether it is an instance of IList<T>.

I'm confused by your description of this method. It sounds like you have a method that does basically what the "as" operator does, except that it somehow fails (throws an exception?) if you pass it "null"? And presumably it also fails somehow if the object doesn't implement IList<T> (also an exception?).

What is this method doing, other than creating a situation where you could have a variable typed as IList<T> and fail to convert the value of that variable to an IList<T>? For example:

IList<T> ToIList<T>(IEnumerable<T> e)
{
IList<T> l = e as IList<T>;

if (e == null || l == null)
throw new InvalidArgumentException();

return l;
}

void MethodA()
{
MethodB(null);
}

void MethodB<T>(IList<T> l)
{
MethodC(l);
}

void MethodC(IEnumerable<T> e)
{
IList<T> l = ToIList(e);
}

The example is only slightly contrived, in that I've explicitly created a sequence of calls guaranteed to fail. Real-life code could legitimately have a null reference in a variable rather than a hard-coded value.

In any case...

If I am calling it
from a method where I know for sure that the IEnumerable<T> is not
null and that it is not IList<T>, why pay for the runtime overhead?

If you already know it's NOT an IList<T>, why would you call such a method at all, regardless of whether it has argument checking or not?

In any case, the more direct answer to your question is: because it complicates the code, and creates runtime overhead for other scenarios (an extra method call for any callers of the public, argument-checking version).

Basically, your in-class code avoids a little bit of overhead, but it forces your client code to suffer _two_ forms of overhead instead of just one.

There are also occasions where I want a method to be available for
both .NET 1 and beyond. So, if I am working with an IEnumerable<T> and
an IEnumerable, I will create a private method:

int count(IEnumerator enumerator)
{
int count = 0;
while (enumerator.MoveNext())
{
++count;
}
return count;
}

I call this method with a generic and non-generic version as so:

int Count<T>(IEnumerable<T> collection)
{ // argument check
using (IEnumerator<T> enumerator = collection.GetEnumerator())
{ return count(enumerator); }
}

int Count(IEnumerable collection)
{ // argument check
return count(collection.GetEnumerator());
}

First, there's already an IEnumerable<T>.Count() extension method. Why write your own? (Especially since the extension method is clever enough to not actually enumerate the entire collection if there's a Count property it can retrieve).

Second, IEnumerable<T> implements IEnumerable. So you don't need a third method for the purpose. You can just have your Count<T>() method call your Count() method and put the implementation there:

int Count<T>(IEnumerable<T> collection)
{
return Count((IEnumerable)collection);
}

int Count(IEnumerable collection)
{
int iRet = 0;

foreach (object obj in collection)
{
iRet++;
}

return iRet;
}

Note that there's also no need to get the enumerator itself. Let the language take care of that for you with the "foreach" statement.

In any case, I don't see how that example explains a policy of never calling a class's public methods from within the class.

Pete
.



Relevant Pages

  • Two questions about efficiency
    ... Near the beginning of the document "Unifying types and classes in ... Python 2.2" by GvR, ... The exception would be when we expect that the requested ... have thought that the overhead to do a key lookup is quite a bit less ...
    (comp.lang.python)
  • Re: Method Design Architecture guidance needed...
    ... The performance hit I was referring to was for built-in exception ... To make an exception processor bullet-proof (e.g., ... overhead beyond a simple context switch. ...
    (comp.object)
  • Re: advice on best use of try catch throw
    ... which presents an additional overhead. ... It encourages less good programmers to ignore possible errors. ... Recurring checks of returnvalues are an unnecessary performance overhead. ... Exception can carry much more information than a HRESULT. ...
    (microsoft.public.vc.language)
  • Re: RTTI overhead
    ... >>overheads (unless an exception is thrown in which case what should be ... >>considered as overhead is not clear). ... directly or otherwise, whatever else happens - stack unwinding, ... <end musing> ...
    (comp.lang.cpp)
  • Re: Interop Marshalling Overhead - Newbie qn
    ... Interop Overhead ... In my application I'm quite comfortable with the interop ... Marshalling Overhead ... int GetDataFromXYZ; ...
    (microsoft.public.dotnet.languages.csharp)