3

All my projects share the same base library that I have build up over quite some time. It contains utilities and static helper classes to assist them where .NET doesn't exactly offer what I want. Originally all the helpers were written mainly to serve an internal purpose and it has to stay that way, but sometimes they prove very useful to other assemblies. Now making them public in a reliable way is more complicated than most would think, for example all methods that assume nullable types must now contain argument checking while not charging internal utilities with the price of doing so. The price might be negligible, but it is far from right.

While refactoring, I have revised this case multiple times and I've come up with the following solutions so far:

  1. Have an internal and public class for each helper
    The internal class contains the actual code while the public class serves as an access point which does argument checking.
    Cons:
    • The internal class requires a prefix to avoid ambiguity (the best presentation should be reserved for public types)
    • It isn't possible to discriminate methods that don't need argument checking
       
  2. Have one class that contains both internal and public members (as conventionally implemented in .NET framework).
    At first, this might sound like the best possible solution, but it has the same first unpleasant con as solution 1.
    Cons:
    • Internal methods require a prefix to avoid ambiguity
       
  3. Have an internal class which is implemented by the public class that overrides any members that require argument checking.
    Cons:
    • Is non-static, atleast one instantiation is required. This doesn't really fit into the helper class idea, since it generally consists of independent fragments of code, it should not require instantiation.
    • Non-static methods are also slower by a negligible degree, which doesn't really justify this option either.

There is one general and unavoidable consequence, alot of maintenance is necessary because every internal member will require a public counterpart.

A note on solution 1: The first consequence can be avoided by putting both classes in different namespaces, for example you can have the real helper in the root namespace and the public helper in a namespace called "Helpers".

3 Answers 3

1

No, this doesn't answer your question directly, but mostly because I believe your problem is rooted elsewhere.

Personally I tend to stay away from "helper classes" and libraries. Such code tends to be a dumping ground for code that doesn't have a home, and leads to poor design. From what I've gathered your suggesting to have an assembly attached to each of your 'real' projects so that it can share some common code. And what I see happening is a bunch of code that gets loaded into the app domain, and maybe 10% of it actually used by any given segment of your domain. Which may end up costing you more than a few checks on public members.

I would suggest analyzing what parts of your domain will actually need specific code shared, and design the reusable code around that. Personally I prefer the usage of extension methods; You can tie specific reusable code to a specific type (avoid extending string, int or the like) giving it a nice comfortable home, and easy to understand code.

When all else fails remember: "Performance is not a problem until it is a problem. Your time is more valuable than a few CPU cycles"

1
  • best post so far
    – toplel32
    Commented Jun 1, 2014 at 12:43
3

You may follow the convention used within the .NET Framework itself.

If you watch the source code of .NET Framework, public methods contain the required checks, then delegate the work itself to the private methods. Those private methods are named identically to the public methods, except that they have a specific suffix ("Internal"). Both methods are within the same class.

For example, System.String has a method Split (line 948), which delegates the work to SplitInternal (line 983).

The fact that the authors use suffixes instead of prefixes is crucial and makes it a very attractive solution: you can easily find the method in the list of methods within a class in Visual Studio, and you don't need to type "InternalS" to get to the Split method which does the job with Intellisense.

8
  • Thanks, but did you read the whole post? I already covered that one.
    – toplel32
    Commented May 23, 2014 at 21:09
  • @toplel32: I don't think you covered that one. You told about prefixes, which is problematic both when listing methods and using Intellisense. Commented May 23, 2014 at 21:15
  • Actually most methods in the .NET library are prefixed with Internal. But that is a minor detail anyway.
    – toplel32
    Commented May 23, 2014 at 21:32
  • @toplel32: I'm curious, since I've never seen any. Can you give an example, please? Commented May 23, 2014 at 21:37
  • I also looked through the linked source, and didn't find any methods suffixed with "Internal." I did see methods marked with the internal access modifier. Commented May 23, 2014 at 21:40
1

Now making them public in a reliable way is more complicated than most would think, for example all methods that assume nullable types must now contain argument checking while not charging internal utilities with the price of doing so. The price might be negligible, but it is far from right.

Why? Because internal code is somehow immune from mistakes?

No, the proper solution is to eat your own dogfood. If the class/function should do bounds checking, then it should do bounds checking for everyone. If the usage is clear enough that "garbage in-garbage out" is an acceptable approach, then it should do that for everyone.

Sure - occasionally you'll run into scenarios where a variant that avoids bounds checking is acceptable and appropriate, but in my experience, those cases tend to need private access, not internal - and they're not the norm. If things are helpful for you, they're almost certainly going to be helpful for everyone. Design them that way.

4
  • No internal code is not immume from mistakes, I figured that was a little sarcastic but I don't need to know the obvious. This is a base library that has been written with care and since I wrote the methods, the chance that I will make mistake is small.
    – toplel32
    Commented May 23, 2014 at 21:29
  • @toplel32 - Because public code isn't written with care? I've heard, "Oh, I don't need to follow best practices because," far too often to think that internal code should be treated special.
    – Telastyn
    Commented May 23, 2014 at 21:44
  • This is rubbish. Aside from .NET, a base library has more dependencies than anything else. If there are any bugs at all, they will surface alot faster. Please spare me a "no code is perfect" rant, we've all heard it before and it doesn't answer the question.
    – toplel32
    Commented May 23, 2014 at 21:47
  • 1
    @toplel32 - No it doesn't answer the question. The other half of my answer "you shouldn't sacrifice quality for performance, so just have public helpers" answers the question. The no code is perfect argument (and the "what is useful for you is useful to others" argument) is what stands behind, explaining why the answer is a good one. And frankly, saying that you can have less quality because you have more users, and they'll find the bugs is... well, let's just say that I wouldn't want to do business that way.
    – Telastyn
    Commented May 23, 2014 at 23:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.