将方法限制为仅由特定类调用
本文关键字:调用 方法 | 更新日期: 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
的一个单例上运行。所以我最后做的是将B
的Func()
移到A
中,并将A.LimitedAccess()
设为私有。还有一些其他细节需要解决,因为总是有,但我得到了一个低影响的解决方案,它给了我A.LimitedAccess()
调用者的编译时错误。
谢谢你的讨论。
No。你唯一能做的就是使LimitedAccess
成为一个私有方法,并在A
类中嵌套B
类。
(我假设您希望所有的类都在同一个程序集中。否则,您可以将A
和B
放在同一个程序集中,将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();
}
}