次の方法で共有


Many Questions: Protected Constructors

Well here it is, one week into my many questions series and I'm already late for the second issue. I have a good reason though, as I took a long weekend to rock climb out at Stone Hill near Eureka Montana. If you are into climbing, I highly recommend it - Stone Hill has a ton of quality routes. I should warn you that some of the ratings are a little stout. My first climb of the weekend was a 5.8 that nearly knocked me on my butt.

OK, onto today's question which comes from the MSDN product feedback center (https://lab.msdn.microsoft.com/ProductFeedback/viewFeedback.aspx?feedbackid=5876d3d0-a4ba-483e-846f-3bb092776b12):

Q: In Version 1 of C#, derived classes could call protected constructors declared in their base classes in any context. In VS 2005, derived classes can only call protected constructors from their base class from their own constructors. For example:

public class Base {
protected Base() {}
}

public class

Derived : Base {
Derived() : base() // call protected constructor from derived constructor ...
// ... still allowed in VS 2005
{}
void Main() {
Base b = new Base(); // call protected constructor to instantiate new object ...
// ... allowed in VS 2003, but error in VS 2005
   }
}

Why restrict access of protected constructors to only derived constructors?

A: To gain some context on this issue, let's quickly review protected access in C#. Members declared with protected accessability can be referenced from the declaring class, and classes derived from the declaring class. However, C# has very different rules for accessing protected static members from derived classes vs. accessing protected instance members from derived classes.

Protected static members can be accessed without restriction from derived classes. No surprises here. Access to protected instance members from derived classes has an additional restriction. When a protected instance member is accessed outside the program text of the class in which it is declared the access is required to take place through an instance of the derived class type in which the access occurs.

Here's an example, showing the various combinations:

public class Base {
protected Base() {}
protected static StaticMethod() {}
protected void InstanceMethod() {}
}

public class

Derived : Base {
Derived() : base() // OK: call protected constructor from derived constructor
// still allowed in VS 2005
{}
void Main() {
Base b = new Base(); // call protected constructor to instantiate new object
                           // allowed in VS 2003, but error in VS 2005
Base.StaticMethod(); // OK: call protected static method
b.InstanceMethod();  // Error: can't call a protected instance method on a Base ...
Derived d = new Derived();
d.InstanceMethod();  // ... but can call an inherited protected instance method on a Derived
   }
}

Essentially, access to protected constructors were (mistakenly) covered by the static members rule in VS 2003, but changed to the instance members rule in VS 2005. So why this change? Better yet, why have the extra instance member rule at all?

The reasoning behind the special instance member rule is that without it, protected access would not actually provide any data encapsulation at all. Protected members would be accessible to anyone that could access public members. Protected access could be circumvented using code similar to this example:

public

class Malicious : Base {
public static void ByPassProtectedAccess(Base b) {
b.InstanceMethod();
}
}

With the 'protected access for instance members' rule, bypassing protected access is not possible for our Malicious class. Programmers use the protected access modifier to control access to the state of their objects to trusted sources. Allowing a way to circumvent access to protected members could result in security vulnerabilities in your code. We've caught security issues very similar to this in the .Net framework code.

Restricting access to inherited protected instance constructors to the 'base' call in derived constructors makes instance contructors fall under the same rule as other instance members, and makes the protected modifier actually provide some access restriction over public access. Unfortunately this change will break some existing C# code. In general, we don't make changes which could break existing C# code unless there's a very good reason - even if it fixes a bug to get the compiler to conform better to the language specification. However, the additional security benefits from plugging the potential unauthorized access of protected instance constructors made this the right decision.

Astute readers will notice that this still leaves protected static members as accessible as public static members. So with that in mind, I recommend never declaring static members to be protected. Always decide if they should be public, private, or internal so that there's no confusion.

Peter
C# Guy

Comments