简体   繁体   English

在编译时限制方法访问的最佳方法是什么?

[英]What is the best way to restrict method access at compile time?

Let's say I have a Manager class 假设我有一个经理班

public class Manager {
   public Item Create() {
      ...
      return new Item(...);
   }
}

and I have an Item class: 我有一个Item类:

public class Item {
   [AllowCallBy(typeof(Manager))]
   public Item(...) {
   }
   ...
}

Now, I would like to use the easiest and most straightforward way to analyze the attributes like AllowCallBy at compile time and display errors or warnings. 现在,我想使用最简单,最直接的方法在编译时分析诸如AllowCallBy之类的属性,并显示错误或警告。 If, in this particular case, a class other than Manager class tries to instantiate Item with new Item(...) I would like to display something like "don't instantiate Item class directly, call Manager.Create(...) instead". 如果在这种特殊情况下,除了Manager类之外的其他类尝试使用new Item(...)实例化Item ,我想显示类似“不要直接实例化Item类,请调用Manager.Create(...)代替”。

I suppose that at least one of the systems: Roslyn, ReSharper, PostSharp or maybe something else would allow me to do it or something that is very close to what I'm trying to achieve. 我想至少有一个系统:罗斯林(Roslyn),ReSharper,PostSharp,或者其他一些方法可以使我做到这一点,或者与我要实现的目标非常接近的事情。 Could somebody give an example of what to use and how to use it? 有人可以举一个使用什么以及如何使用它的例子吗?

This is definitely a code smell as @Habib mentions (can someone link to a specific one?), but without a more complete example it's difficult to offer alternatives beyond what has already been suggested in comments. 就像@Habib提到的那样,这绝对是一种代码味道 (有人可以链接到特定的东西吗?),但是如果没有更完整的示例,很难提供超出注释中建议的替代方法。 I'd encourage you to expand your sample or rethink your design. 我鼓励您扩大样本范围或重新考虑设计。


However, I can present one option that I've used in the past though not for this purpose. 但是,我可以提供过去使用的一个选项,尽管并非出于此目的。 You could mark Item 's constructor as Obsolete : 您可以将Item的构造函数标记为Obsolete

public class Item {
   [Obsolete("Don't instantiate Item class directly, call Manager.Create(...) instead")]
   public Item(...) {
   }
   ...
}

Then in your Manager class, you'd specifically ignore this warning where you invoke the constructor: 然后,在Manager类中,在调用构造函数时,将特别忽略此警告:

public class Manager {
   public Item Create() {
      ...
#pragma warning disable 618
      return new Item(...);
#pragma warning restore 618
   }
}

This way, whenever someone tries to create their own Item elsewhere in the code, they'll get a level 2 CS0618 warning indicating that they should not use the method (note that I didn't say cannot ) with exactly the text entered in the attribute. 这样,每当有人尝试在代码中的其他位置创建自己的Item ,他们都会收到2级CS0618警告,指示他们不应使用该方法(请注意,我不是说不能 )与输入的文本完全相同。属性。 If warnings as errors is enabled (for all warnings or just this one), then it will be a compile error as you originally wanted. 如果启用了警告错误提示 (针对所有警告或仅此警告),则它将是您最初想要的编译错误。

Be aware, nothing prevents others from adding these pragma statements to get around the error. 请注意,没有什么可以阻止其他人添加这些pragma来解决该错误。 However, with this method the developer can't say they didn't know they weren't supposed to use the constructor. 但是,使用这种方法,开发人员不能说他们不知道自己不应该使用构造函数。

Well color me surprised. 好让我惊讶。 PostSharp lets you do exactly what you're looking for . PostSharp使您可以精确地完成所需的工作 In a nutshell, you'd use the ComponentInternalAttribute to control visibility of a type: 简而言之,您将使用ComponentInternalAttribute控制类型的可见性:

public class Item {
   [ComponentInternal(typeof(Manager))]
   public Item(...) {
   }
   ...
}

According to their documentation linked above, attempting to invoke Item 's constructor outside of Manager will yield a compile-time warning: 根据上面链接的文档,尝试在Manager外部调用Item的构造函数将产生编译时警告:

Method Item.ctor cannot be referenced from [some other method] because of the [ComponentInternal] constraint. 由于[ComponentInternal]约束,无法从[某些其他方法]引用方法Item.ctor。

You can make it an error by changing the severity level of the attribute: 您可以通过更改属性的严重性级别来使其错误:

public class Item {
   [ComponentInternal(typeof(Manager), Severity = SeverityType.Error)]
   public Item(...) {
   }
   ...
}

There are way better ways to achieve your goal than your current approach, given that you can actually change that code . 鉴于您实际上可以更改该代码因此有比当前方法更好的方法来实现您的目标。

You could for example mark the contructor of Item class as private and add a static factory method to Item class which would be responsible for creating an instance of the class. 例如,您可以将Item类的构造方法标记为私有,并向Item类添加静态工厂方法,该方法将负责创建该类的实例。

Another way is to move Item class to another assembly, mark its constructor as internal and implement another class (a factory) which would be responsible for creating different Item objects. 另一种方法是将Item类移动到另一个程序集,将其构造函数标记为内部,并实现另一个负责创建不同Item对象的类(工厂)。 Then you class is visible from other assemblies, but it cannot be directly instantiated, so forces the code user to use provided factory. 然后,您的类可以从其他程序集中看到,但是不能直接实例化,因此强制代码用户使用提供的工厂。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM