Why cant partial methods be public if the implementation is in the same assembly?
According to MSDN Documentation for partial classes :
Partial methods are implicitly private
So you can have this
// Definition in file1.cs
partial void Method1();
// Implementation in file2.cs
partial void Method1()
{
// method body
}
But you can't have this
// Definition in file1.cs
public partial void Method1();
// Implementation in file2.cs
public partial void Method1()
{
// method body
}
But why is this? Is there some reason开发者_StackOverflow中文版 the compiler can't handle public partial methods?
Partial methods have to be completely resolvable at compile time. If they are not there at compile time, they are completely missing from the output. The entire reason partial methods work is that removing them has no impact on the API or program flow outside of the one line calling site (which, also, is why they have to return void).
When you add a method to your public API - you're defining a contract for other objects. Since the entire point of a partial method is to make it optional, you'd basically be saying: "I have a contract you can rely on. Oh wait, you can't rely on this method, though."
In order for the public API to be reasonable, the partial method has to really either always be there, or always be gone - in which case it shouldn't be partial.
In theory, the language designers could have changed the way partial methods work, in order to allow this. Instead of removing them from everywhere they were called, they could have implemented them using a stub (ie: a method that does nothing). This would not be as efficient, and is unnecessary for the use case envisioned by partial methods, though.
Instead of asking why are they private, let's rephrase the question to this:
- What would happen if partial methods weren't private?
Consider this simple example:
public partial class Calculator
{
public int Divide(int dividend, int divisor)
{
try
{
return dividend / divisor;
}
catch (DivideByZeroException ex)
{
HandleException(ex);
return 0;
}
}
partial void HandleException(ArithmeticException ex);
}
Let's ignore for the moment the question of why we would make this a partial method as opposed to an abstract one (I'll come back to that). What's important is that this compiles - and works correctly - whether the HandleException
method is implemented or not. If nobody implements it, this just eats the exception and returns 0.
Now let's change the rules, say that the partial method could be protected:
public partial class Calculator
{
// Snip other methods
// Invalid code
partial protected virtual void HandleException(ArithmeticException ex);
}
public class LoggingCalculator : Calculator
{
protected override virtual void HandleException(ArithmeticException ex)
{
LogException(ex);
base.HandleException(ex);
}
private void LogException(ArithmeticException ex) { ... }
}
We have a bit of a problem here. We've "overridden" the HandleException
method, except that there's no method to override yet. And I mean the method literally does not exist, it's not getting compiled at all.
What does it mean what our base Calculator
invokes HandleException
? Should it invoke the derived (overridden) method? If so, what code does the compiler emit for the base HandleException
method? Should it be turned into an abstract method? An empty method? And what happens when the derived method calls base.HandleException
? Is this supposed to just do nothing? Raise a MethodNotFoundException
? It's really hard to follow the principle of least surprise here; almost anything you do is going to be surprising.
Or maybe nothing should happen when HandleException
is invoked, because the base method wasn't implemented. This doesn't seem very intuitive, though. Our derived class has gone and implemented this method and the base class has gone and pulled the rug out from under it without us knowing. I can easily imagine some poor developer pulling his hair out, unable to figure out why his overridden method is never getting executed.
Or maybe this code shouldn't compile at all or should produce a warning. But this has a number of problems of its own. Most importantly, it breaks the contract provided by partial methods, which says that neglecting to implement one should never result in a compiler error. You have a base class which is humming along just fine, and then, by virtue of the fact that someone implemented a totally valid derived class in some completely different part of the application, suddenly your app is broken.
And I haven't even started talking about the possibility of the base and derived classes being in different assemblies. What happens if you reference an assembly with a base class that contains a "public" partial method, and you try to override it in a derived class in another assembly? Is the base method there, or not there? What if, originally, the method was implemented, and we wrote a bunch of code against it, but somebody decided to remove the implementation? The compiler has no way to stub out the partial method calls from referencing classes because as far as the compiler is concerned, that method never existed in the first place. It's not there, it's not in the compiled assembly's IL anymore. So now, simply by removing the implementation of a partial method, which is supposed to have no ill effects, we've gone and broken a whole bunch of dependent code.
Now some people might be saying, "so what, I know that I'm not going to try to do this illegal stuff with partial methods." The thing you have to understand is that partial methods - much like partial classes - are primarily intended to help simplify the task of code generation. It's very unlikely that you would ever want to write a partial method yourself period. With machine-generated code, on the other hand, it's actually fairly likely that consumers of the code will want to "inject" code at various locations, and partial methods provide a clean way of doing this.
And therein lies the problem. If you introduce the possibility of compile-time errors due to partial methods, you've created a situation in which the code generator generates code that doesn't compile. This is a very, very bad situation to be in. Think about what you'd do if your favourite designer tool - say Linq to SQL, or the Winforms or ASP.NET designer, suddenly started producing code that sometimes fails to compile, all because some other programmer created some other class that you've never even seen before that happens to have become a little too intimate with the partial method?
In the end it really boils down to a much simpler question, though: What would public/protected partial methods add that you can't already accomplish with abstract methods? The idea behind partials is that you can put them on concrete classes and they'll still compile. Or rather, they won't compile, but they won't produce an error either, they will just be completely ignored. But if you expect them to be called publicly, then they aren't really "optional" anymore, and if you expect them to be overridden in a derived class, then you might as well just make it abstract or virtual and empty. There isn't really any use for a public or protected partial method, other than to confuse the compiler and the poor bastard trying to make sense of it all.
So instead of opening up that Pandora's box, the team said forget it - public/protected partial methods aren't much use anyway, so just make them private. That way we can keep everything safe and sane. And it is. As long as partial methods stay private, they are easy to understand and worry-free. Let's keep it that way!
Because the MS compiler team did not have a requirement to implement this feature.
Here is a possible scenario of what happen inside MS, because VS uses code gen for a lot of its features, one day a MS code gen developer decided that he/she needed to have partial methods so that the code generated api could be extended by outsiders, and this requirement led the compiler team to act and deliver.
If you don't define the method, what should the compiler do?
If you don't define them, partial methods will not be compiled at all.
This is only possible because the compiler knows where all of the calls to the method are. If the method isn't defined, it will completely remove the lines of code that call the method. (This is also why they can only return void
)
If the method is public, this can't work, because the method might be called by a different assembly, which the compiler has no control over.
Reed's and Slak's answers are entirely correct but you seem unwilling to accept their answers.
I will thus try to explain why partial methods are implemented with these restrictions.
Partial methods are for implementing certain sorts of code gen scenarios with maximal efficiency, both in execution time and in meta data overhead. The last part is the real reason for them since they are attempting to make them (in Erics words) "Pay for play".
When partial methods were added the JIT was entirely capable of inlining an empty method, and thus it having zero runtime effort at the call sites. The problem is that even then there is a cost involved which is that the meta data for the class will have these empty methods increasing their size (needlessly) as well as forcing some more effort during the JITing process to deal with optimizing them away.
Whilst you may not worry too much this cost (and indeed many people won't notice it at all) it does make a big difference to code where startup cost matters, or where disk/memory is constrained. You may have noticed the now mandatory use of .Net on windows mobile 7 and the Zune, in these areas bloat on type metadata is a considerable cost.
Thus partial methods are designed such that, if they are never used they have absolutely zero cost, they cease to exist in the output in any way. This comes with some significant constraints to ensure this does not result in bugs.
Taken from the msdn page with my notes.
- ...the method must return void.
- Partial methods can have ref but not out parameters.
Otherwise removing the call to them may leave you with an undefined problem of what to replace the assignment with.
- Partial methods cannot be extern, because the presence of the body determines whether they are defining or implementing.
- You can make a delegate to a partial method that has been defined and implemented, but not to a partial method that has only been defined.
These follow from the fact the compiler needs to know if it's defined or not, and thus is safe for removal. This leads us to the one you dislike.
- Partial methods are implicitly private, and therefore they cannot be virtual.
Your metal model of this feature is that, if the compiler knows that a partial method is implemented then it should simply allow the partial method to be public (and virtual for that matter) since it can check that you implemented the method.
Were you to change the feature to do this you have two options:
Force all non private partial methods to require implementation.
- simple and not likely to involve much effort but then any such methods are no longer partial in the meaningful sense of the original plan.
Any method declared public is simply deleted if it was not implemented in the assembly.
- This allows the partial removal to be applied
- It requires quite a lot more effort by the compiler (to detect all references to the method rather than simply needing to look in the composed class itself)
- The IntelliSense implementation is a bit confused, should the method be shown? sown only when it's been given a definition?
- Overload resolution becomes much more complex since you need to decide whether a call to such a method with no definition is either a) a compile time failure or b) results in the selection of the next best option if available.
- Side effects within expressions at the call sites are already complex in the private only case. This is mitigated somewhat by the assumption that partial implementations already exhibit a high degree of coupling. This change would increase the potential for coupling.
- This has rather complex failure modes in that public methods could be silently deleted by some innocuous change in the original assembly. Other assemblies depending on this one would fail (at compile time) but with a very confusing error (without considerable effort this would apply even to projects in the same solution).
Solution 1 is simply pointless, it adds effort and is of no benefit to the original goals. Worse it may actually hinder the original goals since someone using partial methods this way may not realise they are gaining no reduction in metadata. Also someone may become confused about the errors that result from failing to supply the definition.
That leaves us with solution 2. This solution involves effort (and every feature starts with -100) so it must add some compelling benefit to get it over the -100 (and the additional negatives added for the confusion caused by not the additional edge cases). What scenarios can you come up with to get things positive?
Your motivating example in the comments above was to "have comments/and or attributes in a different file"
The motivation for XML comments was entirely to increase the locality of documentation and code. If their verbosity is high then outlining exists to mitigate this. My opinion is that this is not sufficient to merit the feature.
The ability to push attributes to a separate location is not in my view that useful, in fact I think having to look in two locations is more confusing. The current private only implementation has this problem too, but it is inevitable and again is mitigated somewhat by the assumption that high coupling that is not visible outside of the class is not as bad as high coupling external to the class.
If you can demonstrate some other compelling reason I'm sure it would be interesting, but the negatives to overcome are considerable.
I've read thorough all your comments and I agree with most of the conclusions, however I have one scenario in which I think will benefit from public/protected partial methods WITHOUT loosing the original intent:
I have a code generator that generates serialization code and other boiler-plate code. In particular, it generates a "Reset" method for every property so that a designer like VS can revert its value to the original. Within the code for this method I generate a call to a partial method Repaint().
The idea is that if the object wants to, it ca write the code for it and then do something, otherwise nothing is done and performance is optimal.
The problem is, sometimes, the Repaint method exists in the object for purposes OTHER than being called from the generated code, at that point, when I declare the method body I should be able to make it internal,protected or public. I am defining the method at this point, and yes, I will documented, etc. here, not in the declaration I generated but in the one I made by hand. The fact that it was also defined in the generated code should not affect this.
I agree that defining access modifiers in a partial method makes no sense when you are declaring the method without a body. When you are declaring the method WITH a body you should be free to declare it any way you want it. I don't see any difference in terms of complexity for the compiler to accept this scenario.
Notice that in partial classes, this is perfectly fine, I can leave one of the partial declarations "naked" without any access modifiers but I can specify them in another partial declaration of the same class. As long as there are no contradictions, everything is fine.
partial
methods will be removed by compiler if it doesn't have an implementation. As per my understanding compiler will not be able to remove the partial
methods if the methods are accessible as public
//partial class with public modifier
public partial class Sample
{
partial void Display();
}
public partial class Sample
{
partial void Display() { }
}
We will be able to access public
methods like below which will be a restriction for compiler to remove the partial
method that is not implemented.
// referred partial method which doesn't have implementation
var sample = new Sample();
sample.Display();
The simple reasons is that partial methods are not public because they are an implementation detail. They are meant primarily to support designer related scenarios and are not ever meant to be part of a supported public API. A non-public method works just fine here.
Allowing partial methods to be public is a feature. Features have inherent costs including design, testing, development, etc ... Partial methods were just one of the many features added in a very packed Visual Studio 2008. They were scoped to be as small as possible to fit the scenario in order to leave room for more pressing features such as LINQ.
Not sure if this will answer your question, it did answer mine. Partial Methods
Excerpts:
If you were allow to return something, and were in turn using that as a return value from your CallMyMethods function, you would run into trouble when the partial methods were not implemented.
精彩评论