67

Let's say I have a need for a simple private helper method, and intuitively in the code it would make sense as an extension method. Is there any way to encapsulate that helper to the only class that actually needs to use it?

For example, I try this:

class Program
{
    static void Main(string[] args)
    {
        var value = 0;
        value = value.GetNext(); // Compiler error
    }

    static int GetNext(this int i)
    {
        return i + 1;
    }
}

The compiler doesn't "see" the GetNext() extension method. The error is:

Extension method must be defined in a non-generic static class

Fair enough, so I wrap it in its own class, but still encapsulated within the object where it belongs:

class Program
{
    static void Main(string[] args)
    {
        var value = 0;
        value = value.GetNext(); // Compiler error
    }

    static class Extensions
    {
        static int GetNext(this int i)
        {
            return i + 1;
        }
    }
}

Still no dice. Now the error states:

Extension method must be defined in a top-level static class; Extensions is a nested class.

Is there a compelling reason for this requirement? There are cases where a helper method really should be privately encapsulated, and there are cases where the code is a lot cleaner and more readable/supportable if a helper method is an extension method. For cases where these two intersect, can both be satisfied or do we have to choose one over the other?

Roman C
  • 49,761
  • 33
  • 66
  • 176
David
  • 208,112
  • 36
  • 198
  • 279
  • 1
    Read what the First Error Message was saying, its wanting you Program Class static, Did you try making it static and attempting your code? – Bearcat9425 May 24 '13 at 16:55
  • Related question : http://stackoverflow.com/questions/2145576/how-can-i-implement-an-extention-property-class-for-primitive-types-in-a-clean-w – cvraman May 24 '13 at 16:57
  • 1
    @Bearcat9425, that would *fix" this specific use case scenario, but not be generally applicable. I imagine this question is for general applicability, not for sample console apps. – Anthony Pegram May 24 '13 at 17:00
  • @Bearcat9425: Interesting, that does work. However, what if the class in question isn't static? In a contrived example it's easy enough to make it static, but in a domain model not so much. – David May 24 '13 at 17:00
  • 2
    I suspect that best you can get is internal static class/internal extension method and hide it in some obscure namespace... – Alexei Levenkov May 24 '13 at 17:03
  • 1
    @AlexeiLevenkov: You're probably right, and in most cases I've ended up with just that by coincidence alone. It would seem that the majority of my "private" helpers are in abstracted (dependency-injected) assemblies the internals of which aren't even visible to the rest of the domain, and it was just easy to make the extensions top-level in that case. Only recently did this come up. – David May 24 '13 at 17:05

7 Answers7

59

Is there a compelling reason for this requirement?

That's the wrong question to ask. The question asked by the language design team when we were designing this feature was:

Is there a compelling reason to allow extension methods to be declared in nested static types?

Since extension methods were designed to make LINQ work, and LINQ does not have scenarios where the extension methods would be private to a type, the answer was "no, there is no such compelling reason".

By eliminating the ability to put extension methods in static nested types, none of the rules for searching for extension methods in static nested types needed to be thought of, argued about, designed, specified, implemented, tested, documented, shipped to customers, or made compatible with every future feature of C#. That was a significant cost savings.

JohnB
  • 18,046
  • 16
  • 98
  • 110
Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • 9
    `"searching for extension methods"` - There's the part I wasn't thinking about right there. It's easy enough to shout from behind the bleachers that "this should work" while forgetting the effort required to make it work. Completely makes sense now, and combined with Alexei's answer arrives at a very workable solution. Thanks! – David May 24 '13 at 17:22
  • 14
    What this says to me is that the design time didn't really design extension methods per se, they designed LINQ. The use cases for extension methods to be private are the same for every other method being private. There's nothing special about extension methods. – Puppy Apr 16 '15 at 09:30
  • 1
    Theoretically this is the right answer. The design team has to set boundaries to what code can do to make it manageable. – DerpyNerd Aug 03 '16 at 06:21
  • 1
    I think this is the best answer. However, I'd hope that with the agility of Roslyn, this could be someday reconsidered, particularly for local functions. Extension methods solve more than just LINQ - they allow "fluent," self-documenting piping of data in a way that traditional function calls don't. I'd personally like type-inferenced lambdas and expression lambdas...little expressive codelets local to a scope. The C# team says that local functions are the chosen alternative to type-inferenced lambdas (delegate ambiguity etc), so here's my vote for investments in loosening this restriction. – lightw8 Aug 17 '18 at 05:44
31

I believe the best you can get in general case is internal static class with internal static extension methods. Since it will be in your own assembly the only people you need to prevent usage of the extension are authors of the assembly - so some explicitly named namespace (like My.Extensions.ForFoobarOnly) may be enough to hint to avoid misuse.

The minimal internal restriction covered in implement extension article

The class must be visible to client code ... method with at least the same visibility as the containing class.

Note: I would make extension public anyway to simplify unit testing, but put in some explicitly named namespace like Xxxx.Yyyy.Internal so other users of the assembly would not expect the methods to be supported/callable. Essentially rely on convention other than compile time enforcement.

Alexei Levenkov
  • 98,904
  • 14
  • 127
  • 179
  • I'll probably be playing with the conventions for a long time to come, eventually arriving at something that makes sense. It's a rare enough case that I'm not terribly worried, it's just a matter of leaving the codebase in an intuitive and supportable state for whoever has to support it in the future (including myself). Thanks! – David May 24 '13 at 17:26
  • Alexei: if it's just for unit testing, wouldn't it be better to leave extension internal and expose internals to your tests project using InternalsVisibleToAttribute (https://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.internalsvisibletoattribute(v=vs.110).aspx)? – Przemo Jan 19 '17 at 18:19
  • @Przemo `InternalsVisibleToAttribute` indeed can be used, but it kills some usability of extension methods as they no longer will show up in autocomplete as far as I know. It is more or less personal choice for most projects (except some libraries designed for very broad usage) - you need to weigh convenience vs. purity of the API in your particular case. Projects I work on generally "small" and not exposing true public APIs - so `public` works fine in my cases. – Alexei Levenkov Jan 19 '17 at 18:27
2

This code compiles and works:

static class Program
{
    static void Main(string[] args)
    {
        var value = 0;
        value = value.GetNext(); // Compiler error
    }

    static int GetNext(this int i)
    {
        return i + 1;
    }
}

Pay attention to static class Program line which was what the compiler said is needed.

Kaveh Shahbazian
  • 13,088
  • 13
  • 80
  • 139
  • 3
    This works fine in my contrived example, I admit. But making the class static isn't always a viable option. For domain models it could cause big problems. – David May 24 '13 at 17:02
  • That's why they are called "Extension Methods" and best practice is to define them in a separate static class, placed under a different namespace. – Kaveh Shahbazian May 24 '13 at 17:04
  • 3
    "Best practices" is a relative term. Encapsulation is also a best practice. There are cases where putting the helper functions (in this case extensions methods) in a separate class (or a completely separate namespace) results in a leaky abstraction, tightly coupling that helper function with the internals of the only object that it helps. – David May 24 '13 at 17:08
  • I am not aware of your domain internals and it most likely you are right. However if they are just some helper functions, then probably they should not be extension methods. Extension Methods meant to provide a statically typed form of duck typing which in turn was needed to implement some sort of higher order type system needed for things like LINQ (a Monad bound to IEnumerable). They have nothing to do with encapsulation. However (again) you can mold it to your will. So that "Best practices" part was something that I myself learnt from others and in practice helped me; and it's relative! – Kaveh Shahbazian May 24 '13 at 17:18
1

I believe it is the way they implemented the compiling of the Extensions Methods.

Looking at the IL, it appears that they add some extra attributes to the method.

.method public hidebysig static int32 GetNext(int32 i) cil managed
{
    .custom instance void [System.Core]System.Runtime.CompilerServices.ExtensionAttribute::.ctor()
    .maxstack 2
    .locals init (
        [0] int32 num)
    L_0000: nop 
    L_0001: ldarg.0 
    L_0002: ldc.i4.1 
    L_0003: add 
    L_0004: dup 
    L_0005: starg.s i
    L_0007: stloc.0 
    L_0008: br.s L_000a
    L_000a: ldloc.0 
    L_000b: ret 
}

There is probably some very fundamental that we are missing that just doesn't make it work which is why the restriction is in place. Could also just be that they wanted to force coding practices. Unfortunately, it just doesn't work and has to be in top-level static classes.

TyCobb
  • 8,909
  • 1
  • 33
  • 53
1

While the question is corretly answered by Alexei Levenkov I would add a simple example. Since the extension methods - if they are private - has no much point, because unreachable outside the containing extension class, you can place them in a custom namespace, and use that namespace only in your own (or any other) namespaces, this makes the extension methods unreachable globally.

namespace YourOwnNameSpace
{
    using YourExtensionNameSpace;

    static class YourClass
    {
        public static void Test()
        {
            Console.WriteLine("Blah".Bracketize());
        }
    }
}

namespace YourOwnNameSpace
{
    namespace YourExtensionNameSpace
    {
        static class YourPrivateExtensions
        {
            public static string Bracketize(this string src)
            {
                return "{[(" + src + ")]}";
            }
        }
    }
}

You have to define your namespace twice, and nest your extension namespace inside the other, not where your classes are, there you have to use it by using. Like this the extension methods will not be visible where you are not using it.

beatcoder
  • 683
  • 1
  • 5
  • 19
1

For anyone coming here that may be looking for an actual answer, it is YES.

You can have private extension methods and here is an example that I am using in a Unity project I am working on right now:

[Serializable]
public class IMovableProps
{
    public Vector3 gravity = new Vector3(0f, -9.81f, 0f);
    public bool isWeebleWobble = true;
}

public interface IMovable
{
    public Transform transform { get; }
    public IMovableProps movableProps { get; set; }
}

public static class IMovableExtension
{
    public static void SetGravity(this IMovable movable, Vector3 gravity)
    {
        movable.movableProps.gravity = gravity;
        movable.WeebleWobble();
    }

    private static void WeebleWobble(this IMovable movable)
    {
        //* Get the Props object
        IMovableProps props = movable.movableProps;
        //* Align the Transform's up to be against gravity if isWeebleWobble is true
        if (props.isWeebleWobble)
        {
            movable.transform.up = props.gravity * -1;
        }
    }
}

I obviously trimmed down quite a bit, but the gist is that this compiles and runs as expected AND it makes sense because I want the extension method to have Access to the WeebleWobble functionality but I don't want it exposed anywhere outside of the static class. I COULD very well configure the WeebleWobble to work in the IMovableProps class but this is just to simply demonstrate that this is possible!

EDIT: If you have Unity and want to test this here is a MonoBehaviour to test it with (This will not actually apply gravity obviously, but it should change the rotation when you check the inverseGravity box in the inspector during play mode)

public class WeebleWobbleTest : MonoBehaviour, IMovable
{
    public bool inverseGravity;
    private IMovableProps _movableProps;
    public IMovableProps movableProps { get => _movableProps; set => _movableProps = value; }

    void Update()
    {
        if (inverseGravity)
        {
            this.SetGravity(new Vector3(0f, 9.81f, 0f);
        }
        else
        {
            this.SetGravity(new Vector3(0f, -9.81f, 0f);
        }
    }
}
  • If I'm not mistaken, extension methods can only be defined inside static classes, so while this works, the limitation has to be taken into account. – bdrajer Oct 21 '21 at 11:55
-3

This is taken from an example on microsoft msdn. Extesnion Methods must be defined in a static class. See how the Static class was defined in a different namespace and imported in. You can see example here http://msdn.microsoft.com/en-us/library/bb311042(v=vs.90).aspx

namespace TestingEXtensions
{
    using CustomExtensions;
    class Program
    {
        static void Main(string[] args)
        {
            var value = 0;
            Console.WriteLine(value.ToString()); //Test output
            value = value.GetNext(); 
            Console.WriteLine(value.ToString()); // see that it incremented
            Console.ReadLine();
        }
    }
}

namespace CustomExtensions 
{
    public static class IntExtensions
    {
        public static int GetNext(this int i)
        {
            return i + 1;
        }
    }
}
Bearcat9425
  • 1,580
  • 1
  • 11
  • 12