Как один тип может получить доступ к частному установщику свойства другого типа? - PullRequest
1 голос
/ 27 октября 2008

Все, что мне нужно, это способ сделать свойство одного класса «устанавливаемым» только из одного другого класса (своего рода класс менеджера).

Возможно ли это даже в c #?

Мой коллега «достоверно» сообщает мне, что у меня есть недостатки в дизайне, но я чувствую, что должен хотя бы спросить сообщество, прежде чем признать свое поражение!

Ответы [ 13 ]

6 голосов
/ 27 октября 2008

Нет, на самом деле это невозможно сделать каким-либо чистым способом в C #. Вы, вероятно, есть недостаток дизайна; -)

5 голосов
/ 27 октября 2008

Вы можете использовать модификатор internal, который позволяет всем типам в одной сборке получать доступ к данным (или назначенным сборкам, если используется [InternalsVisibleTo] - но нет: в C # нет эквивалента friend.

Например:

public string Foo {get; internal set;}
4 голосов
/ 27 октября 2008

У вас есть недостаток дизайна. Кроме того, не будьте параноиком по поводу сокрытия данных. Вот 3,5 способ сделать это:

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);
        }
    }

Вот как мы это сделаем в пред-лямбда-дни:

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;
        }
    }
}
2 голосов
/ 27 октября 2008

Лучший способ сделать это будет:

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

Если у вас есть какое-то сложное ограничение доступа или наследования, и его принудительное выполнение требует слишком большой сложности в коде, иногда лучший способ сделать это - просто правильно прокомментировать его.

Обратите внимание, однако, что вы не можете полагаться на это, если это ограничение имеет некоторые последствия для безопасности, поскольку вы зависите от доброй воли разработчика, который будет использовать этот код.

1 голос
/ 27 октября 2008

Вы можете сделать:

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

Это будет означать, что вы можете использовать указатель 'this' из вызывающего класса. Я бы поставил под сомнение логику того, чего вы пытаетесь достичь, но, не зная сценария, я не могу дать дальнейших рекомендаций. Я скажу следующее: если возможно реорганизовать ваш код, чтобы сделать его более понятным, то это часто стоит делать.

Но это довольно грязно и, конечно, НЕ глупо ... вы были предупреждены!

Alternativly ...

Вы можете передать делегата из класса со свойством (класс A) в класс менеджера (класс B). Делегат может ссылаться на частную функцию внутри A, чтобы B мог вызывать этот делегат как любую обычную функцию. Это исключает, что A знает о B и, возможно, что A создан до B. Опять ... грязно и не защищено от ошибок!

1 голос
/ 27 октября 2008

Или вы можете использовать эти два класса в одной сборке и использовать установщик как внутренний. Я бы проголосовал за недостаток дизайна, если только предыдущий ответ milot (наследующий и защищенный) не имеет смысла.

1 голос
/ 27 октября 2008

Вы не можете сделать это таким образом, но вы можете получить доступ к методу установки свойства из производного класса, чтобы вы могли использовать наследование для этой цели. Все, что вам нужно сделать, это разместить защищенный модификатор доступа. Если вы попытаетесь это сделать, ваш коллега прав :). Вы можете попробовать сделать это так:

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

Имейте в виду, что метод set свойства доступен только из производного класса.

0 голосов
/ 07 января 2013

Если ваша цель - иметь класс Foo, то пусть какое-то свойство (например, Bar, типа Biz) будет изменено каким-либо другим объектом, без публичного раскрытия, простой способ сделать это - иметь экземпляр Foo, который должен быть изменен другим объектом для передачи этому другому объекту Action<Biz>, который указывает на закрытый метод, который изменяет Bar на переданное значение. Другой объект может использовать этот делегат для изменения значения Bar объекта, который его предоставил.

Если кто-то желает дать всем экземплярам какого-либо типа Woozle возможность установить значение Bar для любого экземпляра Foo, вместо того, чтобы предоставлять такие способности для каждого экземпляра, можно потребовать, чтобы Woozle имеет открытый статический метод Woozle.InstallFooBarSetter, который принимает параметр типа Action<Foo, Biz> и один тип Object. Foo должен иметь статический метод WoozleRequestBarSetter, который принимает Object и передает его в Woozle.InstallFooBarSetter вместе с Action<Foo,Biz>. Инициализатор класса для Woozle должен сгенерировать новый Object и передать его в Foo.RequestBarSetter; который передаст объект в Woozle.InstallFooBarSetter вместе с делегатом. Woozle может затем подтвердить, что переданный объект является тем, который он сгенерировал, и - если это так - установить соответствующий делегат. Поступив таким образом, вы гарантируете, что никто, кроме Woozle, не сможет получить делегата (поскольку делегат передается только Woozle.InstallFooBarSetter), а Woozle может быть уверен, что его делегат получен из Foo (поскольку никто другой не будет иметь доступа к объекту, который Woozle создал, и Woozle.InstallFooBarSetter ничего не сделает без него).

0 голосов
/ 27 октября 2008

То, что вы ищете, - это то, что C ++ называет классом Friend, но ни c #, ни vb не обладают этой функциональностью. Существует много споров относительно достоинств такой функциональности, так как она почти поощряет очень сильную связь между классами. Единственный способ реализовать это в c # - это с помощью отражения.

0 голосов
/ 27 октября 2008

Причина, по которой это является недостатком дизайна, заключается в том, что он кажется запутанным между областями действия двух объектов.

Свойства класса должны быть доступны в контексте этого класса, по крайней мере, внутри.

Похоже, что устанавливаемое свойство в вашем классе элементов действительно является свойством класса менеджера.

Вы можете сделать что-то похожее на то, что хотите, тесно связав два класса:

public class MyItem {

    internal MyItemManager manager { get;set; }

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

К сожалению, это тоже не очень хороший дизайн.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...