如何确保一个类可以调用另一个类的方法,但其他类不能调用该方法?

6

我有两个类,希望将它们分别存储在不同的文件中。

namespace GridSystem
{
    public class Grid
    {
        public void AddItem(GridItem item)
        {
            item.InformAddedToGrid();
        }
    }
}

namespace GridSystem
{
    public class GridItem
    {
        public void InformAddedToGrid()
        {
            Debug.Log("I've been added to the grid");
        }
    }
}

我该如何确保其他类不允许调用InformAddedToGrid?

我试图模拟Actionscript名称空间,可以在方法中使用,在public、private、internal等位置。这并不完全保护方法,但强制在访问该方法之前包含命名空间的额外步骤。在C#中有没有其他方法实现这个功能?


将其更改为“private”方法。 - zs2020
3
看起来你在寻找C#中类似于C++ friend 的等价物。我认为这并不存在。 - Mysticial
当它是私有的时候,网格也无法访问它。 - Tseng
@Mysticial 这个不存在。最接近的是 internal - Etienne de Martel
5个回答

5

根据他的API网格,他有将GridItem暴露给外部世界的概念,这是有意义的。 - Keith Nicholas
1
好的,我会在GridItem上放置一个没有定义方法的接口,然后将其暴露给外部世界。这样,您就可以将原始GridItem类中需要暴露给外部世界的内容暴露出来,但仍然隐藏该方法。 - TGH

1
不是说你应该这样做,你应该按照TGH的建议,为GridItem创建一个公共接口,并将gridItem嵌套在Grid中(然后在Grid上创建一个工厂方法来创建Items,并使用部分类将它们放在单独的文件中)。
因为没有一种方法可以有友元方法(你可以通过InternalsVisibleToAttribute实现友元类)
可以这样做(但不要这样做...)
public partial class Grid
{
   public void AddItem(GridItem item)
   {
      item.InformAddedToGrid();
   }
}        

public class GridItem
{
   public void InformAddedToGrid()
   {                
      if (new StackTrace().GetFrame(1).GetMethod().DeclaringType != 
                   typeof(Grid)) throw new Exception("Tantrum!");
      Console.WriteLine("Grid called in...");

   }
}

然后。
 var g = new Grid();
 g.AddItem(new GridItem()); // works
 new GridItem().InformAddedToGrid(); // throws a tantrum...

你是在好奇地问,你是说通常不使用partial,还是当你说“但是不要…”时,你只是指整个示例? - jmstoker
方法内联可能会因为遍历堆栈而使你崩溃。而且这样的方法性能会非常糟糕。 - Mike Zboray
那么你认为if (new StackTrace().GetFrame(1).GetMethod().DeclaringType != typeof(Grid)) throw new Exception("Tantrum!");是个不好的想法? - jmstoker
@jmstoker 是的,我在谈论查找调用类型,这是个不好的想法,但只是为了以防万一有人偶然发现它有用。 - Keith Nicholas
1
我实际上很喜欢这个答案,因为我可以将StackTrace检查包装在条件编译指令中。我只想在我的同事们错误使用类的情况下发出警报,假设他们没有阅读文档。在发布产品中,我不需要执行此检查。这也使得类保持良好的分离。谢谢。 - Jimmie Tyrrell
只是为了澄清,我也不建议大多数人在搜索时遇到这个解决方案。但对我来说有效! - Jimmie Tyrrell

1
一个非常丑陋的答案是将其设置为私有并使用反射。
另一个丑陋的答案是,如果调用者错误,则抛出异常。
这两种方法执行速度比普通调用慢得多。
我认为没有好的答案。C#没有友元。

0

你可以像TGH建议的那样使用嵌套类,只不过是相反的方式。将Grid嵌套在GridItem中,并使InformAddedToGrid为私有。这里我使用了一个嵌套的基类,以便公共API保持不变。请注意,除了您的程序集之外,没有人可以从GridBase继承,因为构造函数是内部的。

public class GridItem
{
    public class GridBase
    {
        internal GridBase() { }

        public void AddItem(GridItem item)
        {
            item.InformAddedToGrid();
        }
    }

    private void InformAddedToGrid()
    {
        Debug.Log("I've been added to the grid");
    }
}

public class Grid : GridItem.GridBase { }

另一种选择是让GridItem显式实现一个内部接口。这样,你的程序集之外的任何人都无法通过接口名称使用它,因此也无法调用InformAddedToGrid方法。

public class Grid
{
    public void AddItem(GridItem item)
    {
        ((IGridInformer)item).InformAddedToGrid();
    }
}

public class GridItem : IGridInformer
{
    void IGridInformer.InformAddedToGrid()
    {
        Debug.Log("I've been added to the grid");
    }
}

internal interface IGridInformer
{
    void InformAddedToGrid();
}

0

我个人认为答案很简单:访问修饰符只是为了提醒程序员类应该是公共的还是私有的。通过反射,你可以解除这些限制。

你对类的使用完全掌握在自己手中:如果你的类只打算在一个地方使用,那就这么做。如果有什么特殊的使用方式,就把它记录下来,放在 XML 注释里。

话虽如此,在这个具体的例子中,我认为由于 GridItem 不会将自己添加到网格中,所以通知网格它已经被添加(“我还没有被添加到网格中”)不是它的工作。我认为 InformAddedToGrid 应该作为 private 方法出现在你的 Grid 类中,因为那里有一个“添加项目”的概念……假设 AddItem(GridItem) 真的是这样做的。


即使InformAddedToGrid是Grid的成员,GridItem仍将具有我希望保留给GridSystem命名空间中的类的公共访问器。例如,InformAddedToGrid可能会修改GridItem的X和Y值。我只希望GridSystem类能够写入X和Y值,而GridSystem之外的类只能读取X和Y值。 - Jimmie Tyrrell
文档可能只能解释到这里。个人而言,对于访问权限,我通常使用Pythonic的方法,但在这种特定情况下,我希望更加防错(这就是为什么我提出了这个问题)。 - Jimmie Tyrrell

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接