views:

642

answers:

12

All I need is a way to make a property of one class only 'settable' from one other class (a sort of manager class).

Is this even possible in c#?

My colleague 'reliably' informs me that I have a design flaw, but I feel I should at least ask the community before I concede defeat!

+6  A: 

No, it's not really possible to do this in any clean way in C#. You probably have a design flaw ;-)

Dave Markle
Does 'probably' indicate that you do not consider your statement reliable? ;-)
Treb
He can do it by putting those two in a separate assembly and using internal. Quit ugly solution though and it is clearly a design flaw. C# is not Java neither C++ as it does not have 'friend' classes.
DrJokepu
Dave Markle
DrJokepu: True, but note the "clean" qualifier in my sentence!
Dave Markle
My example does this in a clean way. You, sir, are wrong. But not about the design flaw thingie.
Will
A: 

if it is a design flaw depends on what you want to do. You could use the StackTrace class from System.Diagnostics to get the Type of the class setting your property and then compare to the type you want to allow setting yor property..but maybe there are better ways for performing something like this (e.g. boxing)

Joachim Kerschbaumer
Good idea but i would much rather enforce the rule at compile time.
Adam Naylor
+1  A: 

You cannot do that on that way, but you can access a property's setter method from a derived class, so you can use inheritance for the purpose. All you have to do is to place protected access modifier. If you try to do so, your colleague is right :). You can try doing it like this:

public string Name
{
    get{ return _name; }
    protected set { _name = value; }
}

keep in mind that the set method of the property is only accessible from the derived class.

milot
Make that ANY derived class. Not a "manager" class.
OJ
A: 

You can achieve to this by making a Public property in your "settable class" that will inherit from the real class that will have a protected property... this way only the inherit class can SET and not class that doesn't inherit. But the drawback is that you will require to have an inherit class...

Daok
In other words -- you have a flaw in your design :)
OJ
It's not a flaw.
Daok
OK, then the design absolutely stinks. Which in my view is a flaw :)
OJ
+1  A: 

Or you could have these two classes in an assembly alone and have the setter as internal. I would vote up for the design flaw though, unless the previous answer by milot (inheriting and protected) makes sense.

mmiika
+5  A: 

You can use the internal modifier, which lets all types in the same assembly access the data (or nominated assemblies if using [InternalsVisibleTo] - but no: there is no friend equivalent in C#.

For example:

public string Foo {get; internal set;}
Marc Gravell
+3  A: 

You have a design flaw. Also, don't be paranoid about data hiding. Here's 3.5's way to do it:

class Program
    {
     static void Main(string[] args)
     {
      Managed m = new Managed();
      Console.WriteLine(m.PrivateSetter);
      m.Mgr.SetProperty("lol");
      Console.WriteLine(m.PrivateSetter);
      Console.Read();
     }
    }

    public class Managed
    {
     private Manager _mgr;
     public Manager Mgr
     {
      get { return _mgr ?? (_mgr = new Manager(s => PrivateSetter = s)); }
     }
     public string PrivateSetter { get; private set; }
     public Managed()
     {
      PrivateSetter = "Unset";
     }
    }

    public class Manager
    {
     private Action<string> _setPrivateProperty;
     public Manager(Action<string> setter)
     {
      _setPrivateProperty = setter;
     }
     public void SetProperty(string value)
     {
      _setPrivateProperty(value);
     }
    }

Here's how we'd do it in pre-lambda days:

public class Managed
{
 private Manager _mgr;
 public Manager Mgr
 {
  get { return _mgr ?? (_mgr = new Manager(this)); }
 }
 public string PrivateSetter { get; private set; }
 public Managed()
 {
  PrivateSetter = "Unset";
 }
 public class Manager
 {
  public void SetProperty(string value)
  {
   m.PrivateSetter = value;
  }
  private Managed m;
  public Manager(Managed man)
  {
   m = man;
  }
 }
}
Will
...... new Manager(s => PrivateSetter = s)); I do not understand this line? Why does it require LINQ?
Daok
Who said it requires anything? And that's not linq; thats a lambda. I'm giving the manager a delegate the manager can use to set the private property in the managed class. Its one of many ways to do this kind of thing.
Will
I haven't vote you down btw. Thx for the pre-lamba that is so more easy to read
Daok
That's k. Learn lambdas. They're shorthand delegates is all. And they are GRRRREAT!
Will
I haven't voted you down either, but that implementation is beastly. The manager has to be created by the class that it is managing?! Yuck :) Design flaw.
OJ
BTW, the manager DOES NOT have to be created by the class it is managing. You can hand it ANY instance of Managed. I just did it that way in the example.
Will
That is so gloriously wrong and nasty; wonderful :)
blowdart
+1  A: 

You could do:

public void setMyProperty(int value, Object caller)
{
    if(caller is MyManagerClass)
    {
        MyProperty = value;
    }
}

This would mean that you could use a 'this' pointer from the calling class. I would question the logic of what you're attempting to achieve, but without knowing the scenario I can't advise any futher. What I will say is this: if it is possible to refactor your code to make it clearer, then it is often worthwhile doing so.

But this is pretty messy and certinly NOT fool-proof ... you have been warned!

Alternativly...

You could pass a delegate from the Class with the Property (Class A) to the Manager Class (Class B). The delegate can refer to a private function within A to allow B to call that delegate as any normal function. This precludes that A knows about B and potentially that A is created before B. Again... messy and not fool-proof!

TK
Anybody could fool that if they had access to the manager's type.
Will
Hence Why I said its NOT fool-proof!
TK
You can make it fool proof... so why not? Other than the fact you'd have to be foolish to want to do this in the first place...
Will
How would you make this fool-proof!? and Yes I would agree that you would need to be foolish before even considering this as a solution!
TK
Look at my answer! Sheesh. Two different ways of doing it. WHAT MORE DO YOU PEOPLE WANT FROM ME???? YOU'RE TEARING ME APART!
Will
+2  A: 

The best way to do it would be:

/// <summary>
/// Gets or sets foo
/// <b>Setter should only be invoked by SomeClass</b>
/// </summary>    
public Object Foo
{
    get { return foo; }
    set { foo = value; }
}

When you have some complex access or inheritance restriction, and enforcing it demands too much complexity in the code, sometimes the best way to do it is just properly commenting it.

Note however that you cannot rely on this if this restriction has some security implications, as you are depending on the goodwill of the developer that will use this code.

Santiago Palladino
A: 

Reflection, though I would agree that having to do this just to get around an access modifier is probably an indication of a bad design.

public class Widget
{
   private int count;
   public int Count
   {
      get { return this.count; }
      private set { this.count = value; }
   }
}

public static class WidgetManager
{
    public static void CatastrophicErrorResetWidgetCount( Widget widget )
    {
       Type type = widget.GetType();
       PropertyInfo info = type.GetProperty("Count",BindingFlags.Instance|BindingFlags.NonPublic);
       info.SetValue(widget,0,null);
    }
}
tvanfosson
A: 

The reason this is a design flaw is because it seems muddled between the scope of the two objects.

The properties of a class should be accessible in the context of that class, at least internally.

It sounds like the settable property on your item class is really a property of the manager class.

You could do something similar to what you want by closely coupling the two classes:

public class MyItem {

    internal MyItemManager manager { get;set; }

    public string Property1 { 
        get { return manager.GetPropertyForItem( this ); } 
    }
}

Unfortunately this isn't great design either.

Keith
A: 

What your looking for is what C++ calls a Friend class but neither c# or vb has this functionality. There is a lot of debate as to the merit of such functionality since it almost encourages very strong coupling between classes. The only way you could implement this in c# would be with reflection.

Aaron Fischer