Instance Accessibilty Modifiers #2718
Replies: 38 comments
-
There is a drawback: extra keywords. And i still don't really see the benefit. Why would you ever want an open private field? |
Beta Was this translation helpful? Give feedback.
-
I'm not entirely clear what the case, let alone a benefit of these suggestions is. |
Beta Was this translation helpful? Give feedback.
-
Personally, I can only see this being useful on methods, to prevent instances of the same type from being able to call private methods in another instance of that type. Which seems like an extremely contrived scenario to me. As for your drawbacks, I think this approach is extremely confusing for reasoning about code - I already have to track public/private/protected/etc, and with this now I need to keep track of what, almost five times the amount of combinations? What do things like If you don't want to be able to access the private fields and methods of another instance, you could easily write a Roslyn analyser to detect this and cause an error. Just look for assignment or call expressions, and check the field/method being assigned for a given attribute. Overall, what problem is this actually trying to solve? It, to me, seems like added complexity to the language for little gain as it can mostly be solved by an analyser. |
Beta Was this translation helpful? Give feedback.
-
Well... thats exactly my point. Right now all private members are open. |
Beta Was this translation helpful? Give feedback.
-
While most of these "accessibility" modifiers could be enforced by the compiler itself, without runtime support there'd be no way to enforce them as they might affect the behavior between assemblies. Specifically, But as a whole I think this introduces a lot of complexity for relatively limited benefit. If you find your projects would benefit from preventing a class from accessing the private/protected members of another instance of that class you can write an analyzer. |
Beta Was this translation helpful? Give feedback.
-
1- Yes exactly. That's a strength of this. You can still achieve the same results as before. class Car{
private int id;
public void ChangeId(Car car, int newId)
{
car.id = newId; // Why is this possible? the other car never knew it's id was changed.
/* Right now there is no autonomy or encapsulation over owned members
towards other objects of the same class*/
}
}
void Main{
Car car1 = new Car();
Car car2 = new Car();
car2.ChangeId(car1);
} |
Beta Was this translation helpful? Give feedback.
-
I'm with others on this one - write an analyzer. |
Beta Was this translation helpful? Give feedback.
-
I don't understand if the complexity everyone is talking about. Is it about how you should program if this feature is added? If that's the case, I have to say that leaving the default to open (meaning every member is open until you chose to make it closed) will leave the use of C# the same as before. Literally no change. I think it's just a tool to facilitate class design. About the analyzer. This would work, but why not add this feature to c#? I mean there is no loss at all. Is it hard to implement? |
Beta Was this translation helpful? Give feedback.
-
https://blogs.msdn.microsoft.com/ericgu/2004/01/12/minus-100-points/ You are misunderstanding language design. The argument is never "why not do this?" The argument is "why do this?". The onus is on the person proposing the language change to justify the value inherent in the change against the enormous costs of even the simplest changes.
The language position on any proposed feature will always start with a strong "No" position. The onus is on the person who wants the feature to convince the language to switch from 'No' to 'Yes'. It's not to convince you that it shouldn't happen. We get that by default :) |
Beta Was this translation helpful? Give feedback.
-
Given that, it's an enormous hill to climb to get something into the language. Imagine if eveyr analyzer feature was just made into a language feature. Imagine the thousands and thousands of disparate little rules and languages tweaks taht everyone wanted trying to get in. The language would quickly become and enormous and confusing mess. Analyzer allow the user to state what they care about in their own domain, without foisting that change onto others who do not care about it. |
Beta Was this translation helpful? Give feedback.
-
Can't argue with that.
And a few others. Maybe this feature is just too weird to see the benefits right now. But just thing of this when making your next class, you might find you could use a closed member. |
Beta Was this translation helpful? Give feedback.
-
I didn't say this feature "doesn't have a good reason to be". I'm saying the onus is on you to convince people with really excellent arguments and usecases what the virtues are of this feature. Basically, no one is going to pick this up and do the legwork for you here :) |
Beta Was this translation helpful? Give feedback.
-
More examples of things that I believe shouldn't be possible were added under the section of Motivation |
Beta Was this translation helpful? Give feedback.
-
It's not a compelling case because it's clearly not representative of real life. Why would a Compare method being changing data taht should be readonly? And, if it is mutable, this wouldn't change the compare method from changing on "this" instance either, which would also be bad. So this seems super contrived as something you need to worry about. |
Beta Was this translation helpful? Give feedback.
-
Thanks for the tip.
If two things are wrong it doesn't mean we shouldn't fix neither. I may be expressing myself poorly, but I believe that an instance should be able to hide it's members from other instances. When working with a team, sometimes there are variables or methods that are not immediately obvious if they should or shouldn't be used from another instance. There are also people for whom this isn't immediately obvious. So for the sake of better structure I think this would be an improvement. |
Beta Was this translation helpful? Give feedback.
-
Note that even though access modifiers can be implicit, lots of coding standards say that they should always be specified. Those coding standards would most likely either 1) forbid the use of your new keywords, or 2) mandate that they are always used. |
Beta Was this translation helpful? Give feedback.
-
@mcosmin222
There are others like that (internal protected i think). But there are also a lot more combinations:
idk man is just a flavor keyword... |
Beta Was this translation helpful? Give feedback.
-
well that would be a pain. |
Beta Was this translation helpful? Give feedback.
-
|
Beta Was this translation helpful? Give feedback.
-
I think that's Step 0 here. If there's no real-world need for this, then there's zero need for people to spend hundreds of hours implementing it, and for all C# developers to spend collectively millions of hours learning it. |
Beta Was this translation helpful? Give feedback.
-
You've never implemented Or you could avoid the mess by making these fields |
Beta Was this translation helpful? Give feedback.
-
I din't get if you were for or against this, but thanks for that example! yeah i guess the way to do it right now is to use readonly, but if you want the variable to be mutable then you have nothing to protect it from "external" change (it's internal since it's the same class but still, it is someone else who might modify it) |
Beta Was this translation helpful? Give feedback.
-
In the case of open, closed, it would be the same as an extra functionality. |
Beta Was this translation helpful? Give feedback.
-
I don't think it's quite so easy to be so black or white about language features. Everything is about tradeoffs, and it starts with the fact that adding any feature is an expensive endeavor. In my experience using C# I can't say I can ever recall finding myself wanting accessibility modifiers like this. I've never felt that being able to access fields from other instances of the same or derived types to be problematic. In fact, I often rely on it, for comparison methods/operators as well as copy constructors, cloning, builders, etc. So this feature would have very limited, if any, benefit for me. That's not to say that there's no benefit. It's like I still think that the best route to follow here would be to create an analyzer. It would be much easier and faster to roll out and would allow for configurable business rules. I'd also like to see some data from real repos that could categorize the number of times one instance accesses or mutates private fields of another. I have a feeling that the frequency of cross-instance mutations is fairly low. |
Beta Was this translation helpful? Give feedback.
-
I agree, i'm sure this doesn't happen that much to be important. But there be juniors out there, pretty dangerous folk! |
Beta Was this translation helpful? Give feedback.
-
'cos what juniors need is more keywords and concepts to learn! |
Beta Was this translation helpful? Give feedback.
-
Thanks for that input, really. What juniors need is to have what they shouldn't be using hidden. Juniors use whatever they can find to get a result, they usually blow out all design and formality out the window. |
Beta Was this translation helpful? Give feedback.
-
Look, you're going to have to teach people how to be good devs. The language can't really do that for you :) |
Beta Was this translation helpful? Give feedback.
-
If your fear is a junior dev might write a method that mutates the private state of a different instance inside an instance method, this implies the dev has access to the source code. What's to prevent the dev from just removing the modifier and doing it anyway? |
Beta Was this translation helpful? Give feedback.
-
I like this idea. This way we can prevent devs from directly accessing obj._value field instead of accessing it via obj.Value property accessors. What I don't like is the extra open/close keyword in addition to public/protected/private access modifier. I'm proposing an alternative idea: Introduce a new access modifer that doesn't allow member access operator to the member. Let's just name that new access modifier "hidden" for the sake of code example: class One {
private int _a; // can be accessed from any instance of One
hidden int _b; // can be accessed from current instance only
private int _c { get; hidden set; } // can be get from any instance of One, but can only be set from current instance
protected int D { get; hidden set; } // can be get from any instance of One and Two, but can only be set from current instance
public int E { get; hidden set; }
public int F { hidden get; set; } // can be set from anywhere, but the value can only be read from current instance
hidden static int _g; // Error: hidden member can't be marked static. (it's the same as private static anyway)
public One(int a, int b) {
_a = a;
this._b = b; // Error: 'this._b' is innacessible due to its protection level.
_b = b; // No error
}
public void CopyTo(One one) {
one._a = a;
one._b = b; // Error: 'this._b' is innaccessible due to its protection level. even if one == this.
}
hidden void IncrementA() {
_a++;
}
public void IncrementA(One one) {
one.IncrementA(); // Error: 'one.IncrementA()' is inaccessible due to its protection level.
}
public void Increment() {
this.IncrementA(); // Error: 'this.IncrementA()' is inaccessible due to its protection level.
IncrementA(); // No error
}
}
class Two : One {
} Other possible keywords for this new access modifier:
|
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Instance Accessibilty Modifiers
Summary
Limit access to type members based on instances. Allowing restrictions based on objects in addition to restrictions based on code, making it possible for an object to encapsulate it’s members from other objects of the same type.
Motivation
From wikipedia on Encapsulation:
Encapsulation is an object-oriented programming concept that binds together the data and functions that manipulate the data, and that keeps both safe from outside interference and misuse. Data encapsulation led to the important OOP concept of data hiding.
On today's OOP, access restrictions brought from structured programming fall short from the possibilities that objects bring us. In addition to the current encapsulation, adding encapsulation by objects could prove quite useful for class design.
If we talk about the principles behind OOP, I don't think I'm the only one thinking that encapsulation is pretty important. If we are able to expand one of the key principles behind OOP, I can't find a reason not to.
For polymorphism, the strength of the design would see an improvement. Allowing for members to be used only by its owners, despite inheritance. Currently if a member is protected any instance of that class could use and modify members of any other instance of that class. This sometimes is desired, but there are cases in which methods and variables should only be accessible by their owner. Right now there is no way of doing this.
It’s more intuitive to think that access to an object’s members should be able to be inaccessible from other objects, instead of from another file. A car shouldn’t be able to manage another’s car color. Nor a person should be able to change another’s name. If a variable is declared private and given a getter and setter for encapsulation, the fact that another instance of that class can bypass those restrictions is, at least partially, a breach in the design.
Edit
Sample cases of current unwanted scenarios
Private members
Right now there is no autonomy or encapsulation over owned members
towards other objects of the same class
Nested Types
Types with nested types suffer from a complete lack of encapsulation. Being possible for nested types to access all of the members on all types up the nested hierarchy. I believe it would be beneficial to be able to change this.
Node structures
Extreme example I know, but this is possible and you can't prevent this in any way. There are no private members between same class instances, that's right no private members. And as far as I understand encapsulation of members should prevent this kind of action.
Detailed design
The Instance Access Modifiers should work in conjunction with current Access Modifiers. Applied on top of them, adding an extra layer of encapsulation.
The starting modifiers proposed are the following*:
*There are other possible modifiers proposed on unresolved questions
open should be the default behavior so nothing changes from previous versions of c#. Being it default
means no need to type any extra keyword and everything stays the same as before.
The possible combinations* of both class restrictions (private, protected, public) and instance restrictions looks as follows:
*internal, protected internal, and private protected omitted due to similar results from previous cases. Same logic still applies.
**Applying the closed modifier to public members will give the same result as using closed protected (“Any class, same instance” would mean only himself can use the member so it ends up being accessible only by “This class and derived. Same instance”) So the public modifier is left unchanged by instance modifiers.
With instance modifiers
This is an example of the possible restrictions and how they operate
On the case of nested types
This could be the one thing that benefits the most out of instance accessibility restrictions. Currently there is no way to hide members from nested types. And sometimes you might need to.
Drawbacks
There is no actual drawback. The only issue that could come from implementing Instance access modifiers is backwards compatibility and people programming without knowing this feature is there. Potentially breaking existing or future code.
But those problems can be easily prevented by making the default instance accessibility open. Which will mean that everything stays the same if no instance accessibility is used. Making open the default modifier would mean no relearning is needed, and no previous knowledge is invalidated.
Alternatives
Since this is a simple design and not the solution of a specific issue, there are no clear alternatives. I believe however, there is room for improvement.
Unresolved questions
Static accessibility
A third and fourth Instance access modifiers that might be useful could limit instance access or static access. Ending up with the following modifiers:
A simple benefit of this, for example, would be seen in Singleton patterns. Declaring the singleton member instance as rigid static, makes it impossible for an instance(object) of the Singleton type to access and/or modify the reference to the main instance directly. Forcing it to use the preferred static property/method instead, which would give an extra security for the pattern.
Type accessibility
This is just a thought, but taking this step towards object based restrictions raises the question of having specific restrictions (or permissions) for specific types/classes. Having the possibility of making a class, a method, or any member inaccessible to everyone but one or a few classes could prove useful for certain design patterns(like MVC, Factory).
Explicitly stating that a member should only be used by someone else that knows how to handle it could prevent an inexperienced or unowing programmer from reinventing the wheel or creating redundant code. Especially now that agile methodologies are being used more often. It’s a common thing to see how a project slowly degrades into a mess when no documentation nor structure is present.
Being able to declare type accessibility could help reduce the frequency and time of refactoring code. And could also prove useful in reducing the need for common knowledge on “how something should work/be used” which is quite common on small software factories.
Design meetings
Nada
Beta Was this translation helpful? Give feedback.
All reactions