【问题标题】:limit method to only be called by a particular class限制方法只能由特定类调用
【发布时间】:2013-05-10 11:43:23
【问题描述】:

我希望一个类中的特定方法只能由特定类访问。例如:

public class A
{
  public void LimitedAccess() {}
  public void FullAccess() {}
}

public class B
{
  public void Func()
  {
     A a = new A();
     a.LimitedAccess();       // want to be able to call this only from class B
  }
} 

public class C
{
  public void Func()
  {
     A a = new A();
     a.FullAccess();           // want to be able to call this method
     a.LimitedAccess();        // but want this to fail compile
  }
} 

我可以使用关键字或属性来强制执行此操作吗?

更新:

由于现有系统的复杂性和时间限制,我需要一个影响较小的解决方案。我想在编译时指出 LimitedAccess() 不能使用。我相信 Jon Skeet 的回答,即我所要求的完全不能在 C# 中完成。

这个问题和 Jon 的回答对于以后可能遇到这个问题的人来说是很好的。这种设计有异味的事实有望使任何人转向选择这样的东西作为理想的解决方案。

正如评论中提到的,如果您试图解决类似的情况,C# friend 对话会很有用。

至于我的特殊解决方案:“为什么 A 会包含 B 的逻辑”(@sysexpand 在 cmets 中询问)。这就是问题所在。 B.Func() 在我正在处理的整个系统中被调用,但它主要在 A 的单例上运行。所以我最终做的是将BFunc() 移动到A 并将A.LimitedAccess() 设为私有。与往常一样,还有一些其他细节需要解决,但我得到了一个影响较小的解决方案,它给了我对A.LimitedAccess() 的调用者的编译时错误。

感谢讨论。

【问题讨论】:

  • 你为什么要这样做?
  • 我有一个大类A,它在整个系统中被广泛使用。 B 类具有其他依赖项,但具有使用 B.LimitedAccess() 的标准/最佳方式。我想尽可能使用B.Func()
  • 请参阅stackoverflow.com/questions/203616/…,您应该将LimitedAccess 标记为内部并通过代码审查发现滥用它。
  • @Jos.Schlitz 恐怕您的代码中存在某种设计错误,我想不出有必要这样做的单一情况。你介意分享一些你的实际代码吗?
  • 一个肮脏的选择是从 A 继承 B 并使 LimitedAccess 受保护。如果它在语义上没有意义,不要。

标签: c#


【解决方案1】:

是的。你所要求的完全有可能。

您可以通过使用接口来限制对特定实例的方法和变量的访问。

但是,单独的接口不能阻止某人创建自己的类实例,此时他们将拥有对该实例的完全访问权限。

为此,接下来您应该将其作为私有类嵌套在另一个类中,以限制对构造函数的访问。

现在您在一个类中有一个特定方法,只能由特定类访问。

在此示例中,只有类 B 能够访问函数 LimitedAccess

public interface IA
{
  void FullAccess();
}

public class B
{
  private class A : IA
  {
    public void LimitedAccess() {}  //does not implement any interface
    public void FullAccess() {}     //implements interface
  } 
    
  private A a = new A();

  public IA GetA()
  {
    return (IA)a;
  }
  
  public void Func()
  {
     /* will be able to call LimitedAccess only from class B, 
        as long as everybody else only has a reference to the interface (IA). */
     a.LimitedAccess();       
  }
} 


//This represents all other classes
public class C
{  
  public void Func(IA ia)
  {
     ia.FullAccess();           // will be able to call this method
     ia.LimitedAccess();        // this will fail to compile
  }
} 

public static class MainClass
{
  public static void Main(string[] args)
  {
    B b = new B();
    b.Func();
          
    IA ia = b.GetA();
      
    C c = new C();
    c.Func(ia);
  }
}

【讨论】:

    【解决方案2】:

    这是@cowlinator 建议的解决方案的变体,使用类AWithUnlimitedAccess 派生自类A,而不是类A 实现接口IA

    结果和限制是相同的,但我更喜欢它,因为 (1) 受限访问方法是在其自己的类中定义的,并且 (2) 添加文档 cmets 更容易。

    public class A
    {
        public void FullAccess() { }
    }
    
    
    public class AWithUnlimitedAccess : A
    {
        public void LimitedAccess() { }
    }
    
    
    public class B
    {
        private AWithUnlimitedAccess a = new AWithUnlimitedAccess();
    
        public A GetA()
        {
            return a;
        }
    
        public void Func()
        {
            a.FullAccess();
            a.LimitedAccess();
        }
    }
    
    // This represents all other classes
    public class C
    {
        public A A;
    
        public void Func()
        {
            A.FullAccess();
            A.LimitedAccess(); // this will fail compile
        }
    }
    
    public static class MainClass
    {
        static void Main(string[] args)
        {
            B b = new B();
            b.Func();
            C c = new C();
            c.A = b.GetA();
            c.Func();
        }
    }
    

    【讨论】:

      【解决方案3】:

      您总是可以通过 StackTrace 看到调用类型。 请注意,在发布模式下构建时,堆栈上的调用将得到优化,堆栈跟踪可能会返回完全不同的类,因此请确保在发布之前对其进行测试。

      /// <summary>
      /// Warning: Any class that calls this other than "B" will throw an exception.
      /// </summary>
      public void LimitedAccess()
      {
           if (new System.Diagnostics.StackTrace().GetFrame(1).GetMethod().DeclaringType != typeof(B)) throw new Exception("Invalid Caller Type, B is only class able to call this method.");
      }
      

      不幸的是,您无法知道它是否在编译时出错。你能做的最好的就是在它被调用时抛出一个异常,并添加一条评论警告人们。

      【讨论】:

        【解决方案4】:

        如果您只是想提醒自己(或队友)不要到处调用LimitedAccess,您可以考虑使用显式接口实现或将LimitedAccess 标记为已过时。

        public interface IA
        {
            void LimitedAccess();
            void FullAccess();
        }
        
        public class A : IA
        {
            private void LimitedAccess() { }
            public void FullAccess() { }
        
            void IA.LimitedAccess() => LimitedAccess();
            void IA.FullAccess() => FullAccess();
        }
        
        public class B
        {
            public void Func()
            {
                IA a = new A();
                a.LimitedAccess();       // want to be able to call this only from class B
            }
        }
        
        public class C
        {
            public void Func()
            {
                A a = new A();
                a.FullAccess();           // want to be able to call this method
                a.LimitedAccess();        // -> fails to compile
            }
        }
        

        【讨论】:

          【解决方案5】:

          也许这是一种解决方法。

          使用 System.Runtime.CompilerServices,然后您可以检查调用函数的名称和/或定义调用函数的文件。如果每个文件都有一个类,则文件名可能是类名的替代品。检查它并阻止呼叫。

          internal void MySecretFunction (string something,
            [CallerMemberName] string memberName = null,
            [CallerFilePath] string filePath = null,
            [CallerLineNumber] int lineNumber = 0) {
              if (!filePath.EndsWith(@"\goodClass.cs")) return;
          
              // else do something
          }
          

          【讨论】:

            【解决方案6】:

            进行这样的设计是违反 OOP 最佳实践的。不应该保护类的方法不被调用。

            如果您的设计需要控制调用方法,则应通过测试参数来进行控制 - 被授权进行调用的调用者将“知道”作为参数传递的魔法词。

            【讨论】:

            • Jon 提供了两种完全合适的方法来做到这一点。另一方面,您的方法绝对是错误的方法。您没有任何编译时支持,它很容易通过使用反编译器被破解或破解,并且该类的用户现在有一个令人困惑的 API 方法来查看他们不应该实际使用的方法。
            • 问题是这样的:“我希望一个类中的特定方法只能由特定类访问。”您只是不要尝试这样做......另一件事是声明一个内部方法,然后从程序集中的所有类中访问它。但这不是问题。
            • 这并不能解决这个问题;该方法仍然可以在任何地方访问,如果在没有正确魔术参数的情况下在任何地方使用它就无法正常工作。 Jon 的两个解决方案实际上都涉及限制那些不应访问的方法的可访问性。
            • 你没有抓住重点。编译器只能测试方法的可见性,但方法的可见性是通用的——它要么对其他类可见,要么不可见。 ONE类没有可见的东西。 Yaur 做对了 - 如果您认为该方法是从代码中的不正确位置调用的,请进行代码审查。如果您不同意,请提出解决方案...您可以尝试使用嵌套类等,但这只是在逃避核心问题 - 问题是糟糕的设计。
            • 各位,感谢有机会如此深入地讨论这个问题。我肯定会继续思考这种编码模式。
            【解决方案7】:

            没有。您唯一能做的就是将LimitedAccess 设为私有方法,并将B 类嵌套在A 类中。

            (我假设您希望所有类都在同一个程序集中。否则您可以将AB 放在同一个程序集中,将C 放在不同的程序集中,然后将LimitedAccess 设为@ 987654328@方法。)

            【讨论】:

            • 其实还有另外一种方法,但这需要具体的类创建对象,并且创建的类将接口实现暴露为out方法(new A(out limitedAccessMethods))。当然,这需要A 中的私有嵌套类,它实现了接口,但只允许创建类访问。但是,从设计的角度来看,它是否很好是值得怀疑的。当然,它是有限的。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2015-06-19
            • 2012-12-08
            • 2015-06-27
            • 1970-01-01
            • 2011-05-08
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多