开发者

How to make a function private to a method?

I'm working on a method that needs to repeat a small operation at different spots, but the code to be repeated should be private to the method. The obvious solution is a nested function. Whatever I try h开发者_如何学Goowever, the C# compiler barfs at me.

Something roughly equal to this Perl snippet:

my $method = sub {
    $helper_func = sub { code to encapsulate };

    # more code

    &$helper( called whenever needed );

    # more code
}

is what I am talking about, and what I'm trying to accomplish in C#.

No other method in the class should be able to access the helper function in this context. The most logical means of writing this construct in C#, as it appears to me would be something like this:

var helper = (/* parameter names */) => { /* code to encapsulate */ };

And actually make the compiler earn its keep.

Since such an assignment is forbidden, as is the equivalent using the older delegate(){} syntax in place of the lambda, and so is declaring a delegate type within a method—what csc actually allows me to write however, is this:

private delegate /* return type */ Helper(/* parameters */);
private /* return type */ method(/* parameters */) {

    Helper helper = (/* parameter names */) => { 
        /* code to encapsulate */
    };

    // more code

    helper( /* called whenever needed */ );

    // more code
}

Which is all fine and dandy for not copy and pasting a chunk of code around and editing the parameters by hand but it leaks a private delegate type to the rest of the class rather than keeping it private to the method. Which defeats the purpose in the first place. Using goto statements and local variables for parameters would provide better encapsulation of "helper" in this context without sacrificing code reuse. If I wanted to simulate function calls by passing parameters through registers, I think would rather use an assembler. I haven't found an acceptable way of refactoring the code to avoid the problem altogether either.

So, is it even possible to force this Common Object Oriented Language to obey?


You actually can do this in C#.

Func<T1, T2, ..., TReturn> myFunc = (a, b, ...) =>
{
  //code that return type TReturn
};


If you need an anonymous method of return type void use Action instead of Func:

Action<T1, T2, ...> myAction = (a, b, ...) =>
{
  //code that doesn't return anything
};


If you are in C# 3.5 or higher you can take advantage of the lambdas and convenience delegate declarations Func<> and Action<>. So for instance

void DoSomething()
{
  Func<int,int> addOne = (ii) => ii +1;
  var two = addOne(1);
}

The reason you can't do

var addOne = (ii) => ii +1;

is because of Homoiconicity, the lambda can be interpreted as two different constructs, a delegate and an expression tree. Thus the need to be explicit in declaration.


If you explicitly type it, it will work, i.e.

Action<paramType1, paramType2> helperAction = (/* parameter names */) => { /* code to encapsulate */ };
Func<paramType1, paramType2, returnType> helperFunction = (/* parameter names */) => { /* code to encapsulate */ };

The reason var doesn't work is that a lambda expression can evaluate to multiple types (I believe either a delegate or expression tree, but don't quote me on that) and the compiler in this situation is unable to infer which was meant.


I recommend looking at the Action<T> and Func<TResult> delegates and their overloads. You can do something like this

static void Main(string[] args)
{
    SomeMethod();
}

private static void SomeMethod()
{
    Action<int> action = (num) => Console.WriteLine(num);

    Enumerable.Range(1,10).ToList().ForEach(action);

    Console.ReadKey();
} 

Here SomeMethod is private and has a local Action<int> delgate that takes an int and does something to it.

I think the issue that you came across is that you can't use implicit typing (i.e. use var) when assigning a lambda expression to a variable.


You can't use the var keyword with lambdas or delegates because they both require additional context information (delegates require a return type, and lambdas require a return type and parameter types). For instance, the (params) => { code } syntax requires to be able to infer the parameter types and return types to work: you do this by explicitly giving it a type.

The generic System.Action delegate type (returns void) could do a good job at what you're trying:

Action<ArgumentType1, ArgumentType2, ...> myDelegate = (params) => { code };

Otherwise, there's also the System.Func, which has a return type, that must be passed as the last generic argument.


It depends on what your definition of hiding is.

The func/action solution (like the one Scott suggests)

void DoSomething()
{
  Func<int,int> addOne = (ii) => ii +1;
  var two = addOne(1);
}

Feals like hidding the method definition when writing regular C# code BUT is when looking at the IL equivalent of

//This is pseudo code but comes close at the important parts
public class Class1
    {
        //The actual type is different from this
        private static Func<int, int> myMethod = AnonymousFunction; 

        public void f()
        {
            myMethod(0);
        }

        private static int AnonymousFunction(int i)
        {
            return 1;
        }
    }

So if you really want to get to the method from outside of the one "hidding" it you can do this with reflection The actual name generated for the field storing the delegate is illegal in C# bul valid in CLR context but that's the only thing that stand in the way of using the delegate as a regular delegate stored in a field (that is if you figue out the name :) )


It's quite simple actually. As the Method seems to have another responsibility than your current Class (why else would you hide this method) move your method into it's own Class and the part you want to have private into a private method in the new class.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜