【问题标题】:How can one type access a private setter of another type's property?一种类型如何访问另一种类型属性的私有设置器?
【发布时间】:2010-09-19 08:56:27
【问题描述】:

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

这在 c# 中是否可行?

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

【问题讨论】:

    标签: c# access-modifiers


    【解决方案1】:

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

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

    【讨论】:

      【解决方案2】:

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

      【讨论】:

        【解决方案3】:

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

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

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

        你可以通过紧密耦合这两个类来做一些你想做的事情:

        public class MyItem {
        
            internal MyItemManager manager { get;set; }
        
            public string Property1 { 
                get { return manager.GetPropertyForItem( this ); } 
            }
        }
        

        很遗憾,这也不是很好的设计。

        【讨论】:

          【解决方案4】:

          反思,尽管我同意为了绕过访问修饰符而不得不这样做可能表明设计不佳。

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

          【讨论】:

            【解决方案5】:

            你可以这样做:

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

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

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

            或者...

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

            【讨论】:

            • 如果他们可以访问经理的类型,任何人都可以愚弄。
            • 因此我说它不是万无一失的!
            • 你可以让它变得万无一失......那为什么不呢?除了你必须愚蠢地想要这样做之外的事实......
            • 你怎么做这个万无一失的!?是的,我同意在考虑将其作为解决方案之前,您需要变得愚蠢!
            • 看我的回答!嘘。两种不同的方法。你还想从我这里得到什么????你把我撕碎了!
            【解决方案6】:

            最好的方法是:

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

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

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

            【讨论】:

              【解决方案7】:

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

              【讨论】:

              • ...... new Manager(s => PrivateSetter = s));我不明白这一行?为什么需要 LINQ?
              • 谁说它需要什么?那不是 linq;那是一个 lambda。我给经理一个委托,经理可以用来在托管类中设置私有属性。它是做这种事情的众多方法之一。
              • 顺便说一句,我还没有投票给你。感谢 pre-lamba 更易于阅读
              • 那是k。学习 lambda。他们只是速记代表。他们太棒了!
              • 我也没有投票给你,但是这个实现非常糟糕。管理器必须由它管理的类创建?!糟糕 :) 设计缺陷。
              【解决方案8】:

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

              例如:

              public string Foo {get; internal set;}
              

              【讨论】:

                【解决方案9】:

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

                【讨论】:

                  【解决方案10】:

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

                  【讨论】:

                  • 换句话说——你的设计有缺陷:)
                  • 好的,那么设计绝对臭。在我看来这是一个缺陷:)
                  【解决方案11】:

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

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

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

                  【讨论】:

                    【解决方案12】:

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

                    【讨论】:

                    • 好主意,但我宁愿在编译时执行规则。
                    【解决方案13】:

                    不,在 C# 中以任何干净的方式执行此操作实际上是不可能的。你可能有设计缺陷;-)

                    【讨论】:

                    • “可能”是否表明您认为您的陈述不可靠? ;-)
                    • 他可以通过将这两个放在一个单独的组件中并使用 internal 来做到这一点。退出丑陋的解决方案,这显然是一个设计缺陷。 C# 既不是 Java,也不是 C++,因为它没有“朋友”类。
                    • 好吧,我看不到他的代码,所以我不知道:-)。在实现 Tubo Encabulator 控制逻辑时,它可能是完全可以接受的。 youtube.com/watch?v=rLDgQg6bq7o&feature=related
                    • DrJokepu:没错,但请注意我句子中的“干净”限定词!
                    • 我的示例以干净的方式执行此操作。先生,你错了。但与设计缺陷无关。
                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 2021-06-13
                    • 1970-01-01
                    • 2019-12-04
                    • 1970-01-01
                    • 2021-07-02
                    • 1970-01-01
                    • 2021-04-11
                    相关资源
                    最近更新 更多