I want to make properties of an object readonly, except for certain classes that I want to allow access to change property values. How do I do this?
class Restricted ()
{
public int Property1{get; }
public int Property2{get; }
}
If I do :
public int Property1{ get; private set;}
How can I choose which class to allow to set this property? check typeof? Is this a valid approach ans secure?
-
You can't, at least not using the language alone. The access modifiers in C# are:
- public: anyone can set the property
- private: only code in this class can set the property
- protected: only code in this class or subclasses can set the property
- internal: only code in this assembly (or InternalsVisibleTo assemblies) can set the property
- protected internal: only code in this class or subclasses, or in this assembly (or InternalsVisibleTo assemblies), can set the property
There's no modifier for "specific classes can set the property" similar to friend declarations in C++.
-
If you control the classes that you want to give access to the setter, you could mark the property's set as "internal" and then compile all your classes into the same assembly. This way, anything else that's using your class won't be able to use the setter.
-
Use an interface in general which only allows read access.
public interface IPublicProperties { int Property1 { get; } int Property2 { get; } } internal class Restricted : IPublicProperties { public int Property1 { get; set; } public int Property2 { get; set; } }The downside is, the class using the setters needs to cast the interface to the actual class. The good thing is, in most of the cases there are benefits of using interfaces in the long run.
itowlson : Unfortunately this still doesn't help to control which code can call the setters -- any class in the same assembly can perform the cast and call the setters, so this offers no more access control than declaring public int Property1 { get; internal set; }. It makes it less *convenient* to call the setters, but it is just as inconvenient for "legitimate" callers as for unwanted ones -- there's no additional barrier to unwanted callers. -
You can create friend assemblies. That is, give access to internal methods of your class to all the classes in another assembly. I don't think you can do it on a class-by-class basis.
-
These ways work in C++, without using the
friendaccess specifier. I'm not sure if they'll work in C#. also, this is just off the top of my head, after seeing your question after a long day, so it may be riddled with errors.One way: the class casts itself to a private base in which the members are mutable. Drawback: any other class can call unlock.
#include <iostream> struct unlocked { int a; int b; //virtual ~unlocked(); // don't call delete on a unlocked }; class locked : private unlocked { public: locked() { a = 0 ; // accessible within locked b = 1 ; } unlocked& unlock( ) { return *this ; } void print( std::ostream& o ) { o << " a = " << a << ", b = " << b << std::endl ; } } ;Usage:
int main() { locked foo ; foo.print( std::cout ); // foo.a = 1 ; illegal foo.unlock().a = 1 ; foo.print( std::cout ) ; std::cout << &foo << " " << &(foo.unlock()) << std::endl; }
Another way: to call unlock, an instance of a class
key' must be passed. Classkeyis a protected inner class of public class 'keyed'. Only classkeyedcan mutate class 'locked. Drawback:lockedmust privately inheritkeyed, to makekeyed::keyvisible tolocked. But because bothkeyedandkeyhave no members, inheriting from them has little overhead. You can usekeyedas is, or better, inherit from it.#include <iostream> class locked ; class keyed ; class keyed { protected: struct key{}; public: void mutateLocked( locked& ); }; struct unlocked { int a; int b; void print( std::ostream& o ) { o << this << " a = " << a << ", b = " << b << std::endl ; } }; class locked : private unlocked, private keyed { public: locked() { a = 0 ; b = 1 ; } unlocked& unlock( keyed::key& k) { return *this ; } using unlocked::print; } ; void keyed::mutateLocked(locked& m ) { key k ; m.print(std::cout) ; unlocked& n = m.unlock( k ) ; n.a = 55; n.b = 77; n.print(std::cout); }Usage:
int main() { keyed k ; //keyed::key kk; // not accessible locked foo ; //unlocked& bar = (unlocked) foo; // not accessible foo.print( std::cout ); //foo.a = 1 ; // not accessible //foo.unlock(kk).a = 1 ; k.mutateLocked(foo); foo.print( std::cout ) ; }
0 comments:
Post a Comment