Friday, April 29, 2011

Can access to an Object''s properties be restricted by accessing Type

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?

From stackoverflow
  • 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 friend access 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. Class key is a protected inner class of public class 'keyed'. Only class keyed can mutate class 'locked. Drawback: locked must privately inherit keyed, to make keyed::key visible to locked. But because both keyed and key have no members, inheriting from them has little overhead. You can use keyed as 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