一种类型如何访问另一种类型的属性的私有设置器?

发布于 2024-07-07 13:59:14 字数 120 浏览 14 评论 0原文

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

这在 C# 中可能吗?

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

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!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(13

前事休说 2024-07-14 13:59:14

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

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

夏尔 2024-07-14 13:59:14

您可以使用 internal 修饰符,它允许同一程序集中的所有类型访问数据(如果使用 [InternalsVisibleTo],则可以访问指定程序集 - 但不行:没有 friend 相当于 C# 中的内容

public string Foo {get; internal set;}

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;}
星光不落少年眉 2024-07-14 13:59:14

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

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;
        }
    }
}
习ぎ惯性依靠 2024-07-14 13:59:14

最好的方法是:

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

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

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

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.

情未る 2024-07-14 13:59:14

您不能以这种方式执行此操作,但您可以从派生类访问属性的 setter 方法,因此您可以使用继承来达到此目的。 您所要做的就是放置protected访问修饰符。 如果您尝试这样做,您的同事是对的:)。 您可以尝试这样做:

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

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

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.

寂寞笑我太脆弱 2024-07-14 13:59:14

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

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.

凌乱心跳 2024-07-14 13:59:14

您可以这样做:

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

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

但这相当混乱,而且肯定不是万无一失的......您已被警告!

或者...

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

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!

优雅的叶子 2024-07-14 13:59:14

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

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...

北恋 2024-07-14 13:59:14

反思,尽管我同意必须这样做只是为了绕过访问修饰符可能表明设计不好。

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

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);
    }
}
↘紸啶 2024-07-14 13:59:14

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

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

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

您可以通过紧密耦合这两个类来完成与您想要的类似的操作:

public class MyItem {

    internal MyItemManager manager { get;set; }

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

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

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.

很酷不放纵 2024-07-14 13:59:14

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

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.

攀登最高峰 2024-07-14 13:59:14

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

如果希望赋予某种类型 Woozle 的所有实例设置 Foo 任何实例的 Bar 值的能力,而不是公开此类基于每个实例的能力,人们可能要求 Woozle 有一个公共静态方法 Woozle.InstallFooBarSetter,该方法采用 Action类型的参数。Object 类型之一。 然后,Foo 应该有一个静态方法 WoozleRequestBarSetter,它接受一个 Object,并将其传递给 Woozle.InstallFooBarSetter 以及一个 ActionWoozle 的类初始值设定项应生成一个新的 Object,并将其传递给 Foo.RequestBarSetter; 它将将该对象与委托一起传递给 Woozle.InstallFooBarSetter。 然后,Woozle 可以确认传入的对象是它生成的对象,如果是,则安装适当的委托。 以这种方式执行操作将确保除了 Woozle 之外没有人可以获得委托(因为委托仅传递给 Woozle.InstallFooBarSetter),并且 Woozle可以确定它的委托来自 Foo (因为没有其他人可以访问 Woozle 创建的对象,并且 Woozle.InstallFooBarSetter 也不会没有它就做任何事情)。

If your goal is to have a class Foo let some property (e.g. Bar, of type Biz) to be changed by some other object, without exposing it publicly, a simple way to do that is to have an instance of Foo which is supposed to be changeable by some other object to pass that other object an Action<Biz> which points to a private method that changes Bar to the passed-in value. The other object may use that delegate to change the Bar value of the object that supplied it.

If one wishes to have give all instances of some type Woozle the ability to set the Bar value of any instance of Foo, rather than exposing such abilities on a per-instance basis, one may require that Woozle have a public static method Woozle.InstallFooBarSetter which takes a parameter of type Action<Foo, Biz> and one of type Object. Foo should then have a static method WoozleRequestBarSetter which takes an Object, and passes it to Woozle.InstallFooBarSetter along with an Action<Foo,Biz>. The class initializer for Woozle should generate a new Object, and pass it to Foo.RequestBarSetter; that will pass the object to Woozle.InstallFooBarSetter along with a delegate. Woozle can then confirm that the passed-in object is the one that it generated, and--if so--install the appropriate delegate. Doing things this way will ensure that nobody but Woozle can get the delegate (since the delegate is only passed to Woozle.InstallFooBarSetter), and Woozle can be sure its delegate comes from Foo (since nobody else would have access to the object that Woozle created, and Woozle.InstallFooBarSetter won't do anything without it).

少女七分熟 2024-07-14 13:59:14

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

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)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文