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:
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;
}
void AdjustLayout() {
AdjustMargins(panelTop);
AdjustMargins(panelButtons);
AdjustMargins(panelProperties);
AdjustMargins(panelOutput);
}
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:
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(panelTop);
adjustMargins(panelButtons);
adjustMargins(panelProperties);
adjustMargins(panelOutput);
}
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:
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;
};
System.Action<FrameworkElement,
System.Action<FrameworkElement>>
adjustCombined =
(FrameworkElement frameworkElement,
System.Action<FrameworkElement> adjustMarginsMethod) => {
if (adjustMarginsMethod != null)
adjustMarginsMethod(frameworkElement);
};
foreach (var element in new FrameworkElement[] {
panelTop,
panelButtons, panelProperties, })
adjustMargins(element);
adjustMargins(panelOutput);
adjustCombined(panelOutput, adjustMargins);
}