将方法限制为仅由特定类调用

本文关键字:调用 方法 | 更新日期: 2023-09-27 18:13:39

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

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#朋友对话是很有用的读物。

至于我的特殊解决方案:"为什么A会包含B的逻辑"(@sysexpand在评论中问)。这就是问题所在。B.Func()在我正在研究的整个系统中都被调用,但它主要在A的一个单例上运行。所以我最后做的是将BFunc()移到A中,并将A.LimitedAccess()设为私有。还有一些其他细节需要解决,因为总是有,但我得到了一个低影响的解决方案,它给了我A.LimitedAccess()调用者的编译时错误。

谢谢你的讨论。

将方法限制为仅由特定类调用

No。你唯一能做的就是使LimitedAccess成为一个私有方法,并在A类中嵌套B类。

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

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

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

然而,接口本身不能阻止某人创建自己的类实例,此时他们将拥有对该实例的完全访问权。

要做到这一点,接下来你应该把它作为一个私有类嵌套在另一个类中,以限制对构造函数的访问。

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

在这个例子中,只有类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);
  }
}

如果您只是想提醒自己(或团队成员)不要到处调用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
    }
}

也许这是一个变通办法。

使用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
}

您总是可以看到带有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.");
}

不幸的是,你无法知道它是否在编译时出现错误。你能做的最好的事情就是在它被调用时抛出一个异常,并添加一个注释来警告人们。

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

如果你的设计需要控制调用一个方法,那么控制应该通过测试参数来实现——被授权进行调用的调用者将"知道"作为参数传递的神奇单词。

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

结果和限制是一样的,但我更喜欢它,因为(1)有限的访问方法是在它自己的类中定义的,(2)更容易添加文档注释。

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