Как реализовать выборочную видимость свойств в С#?

Можно ли сделать свойство класса видимым для публики, но может быть изменено только некоторыми конкретными классами?

например,

// this is the property holder
public class Child
{
    public bool IsBeaten { get; set;}
}

// this is the modifier which can set the property of Child instance
public class Father
{
    public void BeatChild(Child c)
    {
        c.IsBeaten = true;  // should be no exception
    }
}

// this is the observer which can get the property but cannot set.
public class Cat
{
    // I want this method always return false.
    public bool TryBeatChild(Child c)
    {
        try
        {
            c.IsBeaten = true;
            return true;
        }
        catch (Exception)
        {
            return false;
        }
    }

    // shoud be ok
    public void WatchChild(Child c)
    {
        if( c.IsBeaten )
        {
            this.Laugh();
        }
    }

    private void Laugh(){}
}

Ребенок - это класс данных,
Родительский - это класс, который может изменять данные,
Cat - это класс, который может читать только данные.

Есть ли способ реализовать такой контроль доступа с помощью свойства в С#?

Ответ 1

Вместо того, чтобы отображать внутреннее состояние класса Child, вы можете указать вместо этого метод:

class Child {
  public bool IsBeaten { get; private set; }

  public void Beat(Father beater) {
    IsBeaten = true;
  }
}

class Father {
  public void BeatChild(Child child) {
    child.Beat(this);
  }
}

Тогда кошка не может победить вашего ребенка:

class Cat {
  public void BeatChild(Child child) {
    child.Beat(this); // Does not compile!
  }
}

Если другим людям нужно бить ребенка, определите интерфейс, который они могут реализовать:

interface IChildBeater { }

Затем выполните их:

class Child {
  public bool IsBeaten { get; private set; }

  public void Beat(IChildBeater beater) {
    IsBeaten = true;
  }
}

class Mother : IChildBeater { ... }

class Father : IChildBeater { ... }

class BullyFromDownTheStreet : IChildBeater { ... }

Ответ 2

Обычно это достигается с помощью отдельных сборок и InternalsVisibleToAttribute. Когда вы помечаете set классами internal, в текущей сборке будет доступ к нему. Используя этот атрибут, вы можете предоставить доступ к ним другим другим узлам. Помните, используя Reflection, он все равно будет доступен для редактирования.