more advantages or disadvantages to delegate membe

2019-03-09 22:15发布

问题:

class my_class
{
    public int add_1(int a, int b) {return a + b;}
    public func<int, int, int> add_2 = (a, b) => {return a + b;}
}

add_1 is a function whereas add_2 is a delegate. However in this context delegates can forfill a similar role.

Due to precedent and the design of the language the default choice for C# methods should be functions.

However both approaches have pros and cons so I've produced a list. Are there any more advanteges or disadvantages to either approach?

Advantages to conventional methods.

  • more conventional
  • outside users of the function see named parameters - for the add_2 syntax arg_n and a type is generally not enough information.
  • works better with intellisense - ty Minitech
  • works with reflection - ty Minitech
  • works with inheritance - ty Eric Lippert
  • has a "this" - ty CodeInChaos
  • lower overheads, speed and memory - ty Minitech and CodeInChaos
  • don't need to think about public\private in respect to both changing and using the function. - ty CodeInChaos
  • less dynamic, less is permitted that is not known at compile time - ty CodeInChaos

Advantages to "field of delegate type" methods.

  • more consistant, not member functions and data members, it's just all just data members.
  • can outwardly look and behave like a variable.
  • storing it in a container works well.
  • multiple classes could use the same function as if it were each ones member function, this would be very generic, concise and have good code reuse.
  • straightforward to use anywhere, for example as a local function.
  • presumably works well when passed around with garbage collection.
  • more dynamic, less must be known at compile time, for example there could be functions that configure the behaviour of objects at run time.
  • as if encapsulating it's code, can be combined and reworked, msdn.microsoft.com/en-us/library/ms173175%28v=vs.80%29.aspx
  • outside users of the function see unnamed parameters - sometimes this is helpfull although it would be nice to be able to name them.
  • can be more compact, in this simple example for example the return could be removed, if there were one parameter the brackets could also be removed.
  • roll you'r own behaviours like inheritance - ty Eric Lippert
  • other considerations such as functional, modular, distributed, (code writing, testing or reasoning about code) etc...

Please don't vote to close, thats happened already and it got reopened. It's a valid question even if either you don't think the delegates approach has much practical use given how it conflicts with established coding style or you don't like the advanteges of delegates.

回答1:

First off, the "high order bit" for me with regards to this design decision would be that I would never do this sort of thing with a public field/method. At the very least I would use a property, and probably not even that.

For private fields, I use this pattern fairly frequently, usually like this:

class C
{
    private Func<int, int> ActualFunction = (int y)=>{ ... };
    private Func<int, int> Function = ActualFunction.Memoize();

and now I can very easily test the performance characteristics of different memoization strategies without having to change the text of ActualFunction at all.

Another advantage of the "methods are fields of delegate type" strategy is that you can implement code sharing techniques that are different than the ones we've "baked in" to the language. A protected field of delegate type is essentially a virtual method, but more flexible. Derived classes can replace it with whatever they want, and you have emulated a regular virtual method. But you could build custom inheritence mechanisms; if you really like prototype inheritance, for example, you could have a convention that if the field is null, then a method on some prototypical instance is called instead, and so on.

A major disadvantage of the methods-are-fields-of-delegate-type approach is that of course, overloading no longer works. Fields must be unique in name; methods merely must be unique in signature. Also, you don't get generic fields the way that we get generic methods, so method type inference stops working.



回答2:

The second one, in my opinion, offers absolutely no advantage over the first one. It's much less readable, is probably less efficient (given that Invoke has to be implied) and isn't more concise at all. What's more, if you ever use reflection it won't show up as being a method so if you do that to replace your methods in every class, you might break something that seems like it should work. In Visual Studio, the IntelliSense won't include a description of the method since you can't put XML comments on delegates (at least, not in the same way you would put them on normal methods) and you don't know what they point to anyway, unless it's readonly (but what if the constructor changed it?) and it will show up as a field, not a method, which is confusing.

The only time you should really use lambdas is in methods where closures are required, or when it's offers a significant convenience advantage. Otherwise, you're just decreasing readability (basically the readability of my first paragraph versus the current one) and breaking compatibility with previous versions of C#.



回答3:

Why you should avoid delegates as methods by default, and what are alternatives:

Learning curve

Using delegates this way will surprise a lot of people. Not everyone can wrap their head around delegates, or why you'd want to swap out functions. There seems to be a learning curve. Once you get past it, delegates seem simple.

Perf and reliability

There's a performance loss to invoking delegates in this manner. This is another reason I would default to traditional method declaration unless it enabled something special in my pattern.

There's also an execution safety issue. Public fields are nullable. If you're passed an instance of a class with a public field you'll have to check that it isn't null before using it. This hurts perf and is kind of lame.

You can work around this by changing all public fields to properties (which is a rule in all .Net coding standards anyhow). Then in the setter throw an ArgumentNullException if someone tries to assign null.

Program design

Even if you can deal with all of this, allowing methods to be mutable at all goes against a lot of the design for static OO and functional programming languages.

In static OO types are always static, and dynamic behavior is enabled through polymorphism. You can know the exact behavior of a type based on its run time type. This is very helpful in debugging an existing program. Allowing your types to be modified at run time harms this.

In both static OO and function programming paradigms, limiting and isolating side-effects is quite helpful, and using fully immutable structures is one of the primary ways to do this. The only point of exposing methods as delegates is to create mutable structures, which has the exact opposite effect.

Alternatives

If you really wanted to go so far as to always use delegates to replace methods, you should be using a language like IronPython or something else built on top of the DLR. Those languages will be tooled and tuned for the paradigm you're trying to implement. Users and maintainers of your code won't be surprised.

That being said, there are uses that justify using delegates as a substitute for methods. You shouldn't consider this option unless you have a compelling reason to do so that overrides these performance, confusion, reliability, and design issues. You should only do so if you're getting something in return.

Uses

  • For private members, Eric Lippert's answer describes a good use: (Memoization).

  • You can use it to implement a Strategy Pattern in a function-based manner rather than requiring a class hierarchy. Again, I'd use private members for this...

...Example code:

public class Context
{
    private Func<int, int, int> executeStrategy;

    public Context(Func<int, int, int> executeStrategy) {
        this.executeStrategy = executeStrategy;
    }

    public int ExecuteStrategy(int a, int b) {
        return executeStrategy(a, b);
    }
}
  • I have found a particular case where I think public delegate properties are warrented: To implement a Template Method Pattern with instances instead of derived classes...

...This is particularly useful in automated integration tests where you have a lot of setup/tear down. In such cases it often makes sense to keep state in a class designed to encapsulate the pattern rather than rely on the unit test fixture. This way you can easily support sharing the skeleton of the test suite between fixtures, without relying on (sometimes shoddy) test fixture inheritance. It also might be more amenable to parallelization, depending on the implementation of your tests.

var test = new MyFancyUITest
{
    // I usually name these things in a more test specific manner...
    Setup = () => { /* ... */ },
    TearDown = () => { /* ... */ },
};
test.Execute();

Intellisense Support

outside users of the function see unnamed parameters - sometimes this is helpfull although it would be nice to be able to name them.

Use a named delegate - I believe this will get you at least some Intellisense for the parameters (probably just the names, less likely XML docs - please correct me if I'm wrong):

public class MyClass
{
    public delegate int DoSomethingImpl(int foo, int bizBar);
    public DoSomethingImpl DoSomething = (x, y) => { return x + y; }
}


回答4:

I'd avoid delegate properties/fields as method replacements for public methods. For private methods it's a tool, but not one I use very often.

  • instance delegate fields have a per instance memory cost. Probably a premature optimization for most classes, but still something to keep in mind.

  • Your code uses a public mutable field, which can be changed at any time. That hurts encapsulation.

  • If you use the field initializer syntax, you can't access this. So field initializer syntax is mainly useful for static methods.

  • Makes static analysis much harder, since the implementation of that method isn't known at compile-time.

There are some cases where delegate properties/fields might be useful:

  • Handlers of some sort. Especially if multi-casting (and thus the event subscription pattern) doesn't make much sense
  • Assigning something that can't be easily described by a simple method body. Such as a memoized function.
  • The delegate is runtime generated or at least its value is only decided at runtime


回答5:

Using a closure over local variables is an alternative to using a method and private fields. I strongly dislike classes with lots of fields, especially if some of these fields are only used by two methods or less. In these situations, using a delegate in a field can be preferable to conventional methods

class MyClassConventional {
  int? someValue; // When Mark() is called, remember the value so that we can do something with it in Process(). Not used in any other method.
  int X;

  void Mark() {
    someValue = X;
  }

  void Process() {
    // Do something with someValue.Value
  }
}

class MyClassClosure {
  int X;

  Action Process = null;

  void Mark() {
    int someValue = X;
    Process = () => { // Do something with someValue };
  }
}


回答6:

This question presents a false dichotomy - between functions, and a delegate with an equivalent signature. The main difference is that one of the two you should only use if there are no other choices. Use this in your day to day work, and it will be thrown out of any code review.

The benefits that have been mentioned are far outweighed by the fact that there is almost never a reason to write code that is so obscure; especially when this code makes it look like you don't know how to program C#.

I urge anyone reading this to ignore any of the benefits which have been stated, since they are all overwhelmed by the fact that this is the kind of code that demonstrates that you do not know how to program in C#.

The only exception to that rule is if you have a need for one of the benefits, and that need can't be satisfied in any other way. In that case, you'll need to write more comment than code to explain why you have a good reason to do it. Be prepared to answer as clearly as Eric Lippert did. You'd better be able to explain as well as Eric does that you can't accomplish your requirements and write understandable code at the same time.