1

我所需要的只是一种使一个类的属性只能从另一个类(一种管理器类)“设置”的方法。

这在 C# 中甚至可能吗?

我的同事“可靠”地告诉我我有一个设计缺陷,但我觉得我至少应该在认输之前询问社区!

4

13 回答 13

6

不,在 C# 中不可能以任何干净的方式做到这一点。您可能有设计缺陷;-)

于 2008-10-27T11:48:28.070 回答
6

您可以使用internal修饰符,它允许同一程序集中的所有类型访问数据(或指定程序集,如果使用[InternalsVisibleTo]- 但不是:C# 中没有friend等效项。

例如:

public string Foo {get; internal set;}
于 2008-10-27T11:54:15.827 回答
4

你有一个设计缺陷。另外,不要对数据隐藏感到偏执。这是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);
        }
    }

以下是我们在 lambda 之前的日子里是如何做到的:

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;
        }
    }
}
于 2008-10-27T11:55:52.300 回答
2

最好的方法是:

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

当您有一些复杂的访问或继承限制,并且执行它需要在代码中过于复杂时,有时最好的方法就是正确地注释它。

但是请注意,如果此限制具有一些安全隐患,则不能依赖此,因为您取决于将使用此代码的开发人员的善意。

于 2008-10-27T12:07:26.673 回答
1

你不能那样做,但你可以从派生类访问属性的 setter 方法,所以你可以使用继承来达到这个目的。您所要做的就是放置受保护的访问修饰符。如果您尝试这样做,那么您的同事是对的:)。您可以尝试这样做:

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

请记住,该属性的 set 方法只能从派生类访问。

于 2008-10-27T11:52:14.507 回答
1

或者您可以将这两个类单独放在一个程序集中,并将 setter 作为内部。不过,我会投票支持设计缺陷,除非 milot 先前的回答(继承和保护)是有道理的。

于 2008-10-27T11:54:00.133 回答
1

你可以这样做:

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

这意味着您可以使用调用类中的“this”指针。我会质疑你试图实现的逻辑,但在不知道场景的情况下,我无法提供任何进一步的建议。我要说的是:如果可以重构代码以使其更清晰,那么这样做通常是值得的。

但这很混乱,而且肯定不是万无一失的……您已被警告过!

或者...

您可以将具有属性的类(A 类)的委托传递给管理器类(B 类)。委托可以引用 A 中的私有函数,以允许 B 像任何普通函数一样调用该委托。这排除了 A 知道 B 并可能 A 是在 B 之前创建的。再次......混乱而不是万无一失!

于 2008-10-27T11:57:11.187 回答
0

您可以通过在“可设置类”中创建一个公共属性来实现这一点,该属性将从具有受保护属性的真实类继承......这样只有继承类可以设置,而不是不继承的类。但缺点是你需要有一个继承类......

于 2008-10-27T11:53:46.170 回答
0

反射,虽然我同意这样做只是为了绕过访问修饰符可能表明设计不好。

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);
    }
}
于 2008-10-27T12:32:37.887 回答
0

这是一个设计缺陷的原因是因为它似乎混淆了两个对象的范围。

类的属性应该可以在该类的上下文中访问,至少在内部是可访问的。

听起来您的项目类上的可设置属性实际上是管理器类的属性。

您可以通过紧密耦合这两个类来做与您想要的类似的事情:

public class MyItem {

    internal MyItemManager manager { get;set; }

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

不幸的是,这也不是很好的设计。

于 2008-10-27T12:42:33.797 回答
0

您正在寻找的是 C++ 调用的 Friend 类,但 c# 或 vb 都没有此功能。关于这种功能的优点存在很多争论,因为它几乎鼓励类之间非常强的耦合。在 c# 中实现这一点的唯一方法是使用反射。

于 2008-10-27T14:06:09.540 回答
0

如果您的目标是让一个类Foo让某个属性(例如Bartype Biz)被某个其他对象更改,而不公开地公开它,那么一种简单的方法是拥有一个Foo应该可以被某些人更改的实例其他对象传递另一个对象,Action<Biz>该对象指向一个私有方法,该方法更改Bar为传入的值。另一个对象可以使用该委托来更改Bar提供它的对象的值。

如果希望赋予某种类型Woozle的所有实例设置 的Bar任何实例的值的能力Foo,而不是在每个实例的基础上公开这些能力,则可能需要Woozle具有一个公共静态方法Woozle.InstallFooBarSetter,该方法采用类型参数Action<Foo, Biz>和类型之一ObjectFoo然后应该有一个静态方法WoozleRequestBarSetter,它接受一个Object,并将它Woozle.InstallFooBarSetter与一个Action<Foo,Biz>. 的类初始化程序Woozle应该生成一个新的Object,并将其传递给Foo.RequestBarSetter; 这会将对象Woozle.InstallFooBarSetter与委托一起传递给。 Woozle然后可以确认传入的对象是它生成的对象,并且 - 如果是这样 - 安装适当的委托。以这种方式做事将确保没有人Woozle可以获取委托(因为委托仅传递给Woozle.InstallFooBarSetter),并且Woozle可以确定其委托来自Foo(因为没有其他人可以访问Woozle创建的对象,并且Woozle.InstallFooBarSetter不会做任何事情没有它)。

于 2013-01-07T16:55:18.347 回答
-1

如果它是一个设计缺陷取决于你想要做什么。您可以使用 System.Diagnostics 中的 StackTrace 类来获取设置您的属性的类的类型,然后与您希望允许设置您的属性的类型进行比较..但也许有更好的方法来执行这样的事情(例如拳击)

于 2008-10-27T11:50:48.627 回答