I'm porting some Java code to C#, and I ran into this idiom used to copy objects:
class Base
{
int x;
public Base(int x) { this.x = x; }
protected Base(Base other) { x = other.x; }
}
class Derived : Base
{
Base foo;
public Derived(Derived other)
: base(other)
{
foo = new Base(other.foo); // Error CS1540
}
}
Error CS1540 being:
Cannot access protected member 'Base.Base(Base)' via a qualifier of type 'Base'; the qualifier must be of type 'Derived' (or derived from it)
I understand the purpose of this error: it prevents accessing protected members of sibling types. But Base.Base(Base) is obviously not going to be invoked on a sibling type! Is this simply not included in the spec, or am I missing some reason why this would not be safe?
EDIT: Gah, the idiom was new Base(other.foo)
not new Base(other)
Sections 3.5.2 and 3.5.3 of the language specification spell this out, I'll post 3.5.2 for convenience (it's shorter!) and let you find 3.5.3 on your own.
In intuitive terms, when a type or member M is accessed, the following steps are evaluated to ensure that the access is permitted:
- First, if M is declared within a type (as opposed to a compilation unit or a namespace), a compile-time error occurs if that type is not accessible.
- Then, if M is public, the access is permitted.
- Otherwise, if M is protected internal, the access is permitted if it occurs within the program in which M is declared, or if it occurs within a class derived from the class in which M is declared and takes place through the derived class type (§3.5.3).
- Otherwise, if M is protected, the access is permitted if it occurs within the class in which M is declared, or if it occurs within a class derived from the class in which M is declared and takes place through the derived class type (§3.5.3).
- Otherwise, if M is internal, the access is permitted if it occurs within the program in which M is declared.
- Otherwise, if M is private, the access is permitted if it occurs within the type in which M is declared.
- Otherwise, the type or member is inaccessible, and a compile-time error occurs.
Basically, access to protected members of the base have to be through instances of the derived. As you said, siblings cannot access each others' protected members, but the language specification also prohibits children from accessing protected members of the base unless the reference is through the child.
I you could do this, then you could always trivially invoke protected
members of any class that allowed you to derive from it, even though the derived class is not used. This totally subverts the security of the protected
mechanism.
For example, let's say we derive from Base
with Derived
as above. If the rules weren't as they are, you could add a method like this to Derived
:
public static void CallProtectedMethod(Base baseInstance)
{
baseInstance.ProtectedMethod();
}
and now anyone can invoke it like this:
Derived.CallProtectedMethod(baseInstance);
while the direct approach fails:
baseInstance.ProtectedMethod();
In this case baseInstance
might really be of type Base
and have nothing to do with Derived
. To prevent this and ensure that protected
methods stay protected
unless the instance really is of the Derived
type, calling these methods through a different instance is illegal.
Likewise for protected
constructors:
public static Base CreateProtectedBase()
{
return new Base();
}
and now anyone can invoke it like this:
var baseInstance = Derived.CreateProtectedBase();
while the direct approach fails:
var baseInstance = new Base();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With