Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Hide Ad-hoc Methods Inside the Calling Method’s Body

4.62/5 (8 votes)
21 Dec 2010CPOL1 min read 17.2K  
Using anonymous methods helps to limit visibility of code callable just from within one method
Why would anyone restrict any code visibility and scope inside an assembly? Well, by the same reasons limited variable scope is used. The users of Pascal and Delphi (major predecessor of .NET) miss its local methods -- local to the scope of some other method. However, this feature is actually available with .NET and C#.

Let's consider the following code:

C#
void AdjustMargins(FrameworkElement frameworkElement) {
    Thickness margin = frameworkElement.Margin;
    Control parent = frameworkElement.Parent as FrameworkElement;
    if (parent == null) return;
    if (parent.Margin.Left > 0) margin.Left = 0;
    if (parent.Margin.Right > 0) margin.Right = 0;
    if (parent.Margin.Top > 0) margin.Left = 0;
    if (parent.Margin.Bottom > 0) margin.Bottom = 0;
    //..
    frameworkElement.Margin = margin;
} //AdjustMargins

void AdjustLayout() {
    AdjustMargins(panelTop);
    AdjustMargins(panelButtons);
    AdjustMargins(panelProperties);
    AdjustMargins(panelOutput);
    //..
} //AdjustLayout


It does not really matter what it does. What's wrong in this code?

The major reason method AdjustMargins code is created as a separate method is elementary code reuse because it is called from the body of the method AdjustLayout more than once. At the same time, this sample suggests that AdjustMargins is an ad-hoc method called only from AdjustLayout. So, this code is just fine, but it could be a bit more manageable if the callable code of AdjustMargins could be placed entirely into the scope and made completely invisible outside of this scope.

If the Window class being developed is small enough, it does not matter, but what if it is already overwhelmed with other methods (or tends to be overwhelmed later down the road)? In this case, reducing number of methods visible at the level of the class really helps to keep this class manageable.

This is where local methods would help a bit, but there is no such thing in .NET -- with the exclusion of anonymous method. With the anonymous methods, one could achieve the same effect as with local methods. We need to use the following pattern:

C#
void AdjustLayout() {
    System.Action<FrameworkElement> adjustMargins =
        (FrameworkElement frameworkElement) => {
        Thickness margin = frameworkElement.Margin;
        Control parent = frameworkElement.Parent as FrameworkElement;
        if (parent == null) return;
        if (parent.Margin.Left > 0) margin.Left = 0;
        if (parent.Margin.Right > 0) margin.Right = 0;
        if (parent.Margin.Top > 0) margin.Left = 0;
        if (parent.Margin.Bottom > 0) margin.Bottom = 0;
        //...
        frameworkElement.Margin = margin;
    }; //adjustMargins
    adjustMargins(panelTop);
    adjustMargins(panelButtons);
    adjustMargins(panelProperties);
    adjustMargins(panelOutput);
    //...
} //AdjustLayout


What if some code should be local but shared by two or more calling methods? Well, delegates are first-class objects, so they can be passed as parameters. Here is a more complex example:

C#
void AdjustLayout() {
    System.Action<FrameworkElement> adjustMargins =
        (FrameworkElement frameworkElement) => {
        Thickness margin = frameworkElement.Margin;
        Control parent = frameworkElement.Parent as FrameworkElement;
        if (parent == null) return;
        if (parent.Margin.Left > 0) margin.Left = 0;
        if (parent.Margin.Right > 0) margin.Right = 0;
        if (parent.Margin.Top > 0) margin.Left = 0;
        if (parent.Margin.Bottom > 0) margin.Bottom = 0;
        //...
        frameworkElement.Margin = margin;
    }; //adjustMargins
    System.Action<FrameworkElement,
    System.Action<FrameworkElement>>
        adjustCombined =
        (FrameworkElement frameworkElement, 
        System.Action<FrameworkElement> adjustMarginsMethod) => {
        if (adjustMarginsMethod != null)
            adjustMarginsMethod(frameworkElement);
        //other adjustments...
    };
    foreach (var element in new FrameworkElement[] {
        panelTop,
        panelButtons, panelProperties, })
        adjustMargins(element);
    adjustMargins(panelOutput);
    adjustCombined(panelOutput, adjustMargins);
    //...
} //AdjustLayout

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)