在編譯時限制方法訪問的最佳方法是什么?

[英]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. 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".

現在,我想使用最簡單,最直接的方法在編譯時分析AllowCallBy等屬性並顯示錯誤或警告。如果在這種特殊情況下,Manager類以外的類嘗試使用新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. Could somebody give an example of what to use and how to use it?

我認為至少有一個系統:Roslyn,ReSharper,PostSharp或者其他東西可以讓我做到這一點或者與我想要達到的非常接近的東西。有人可以舉例說明使用方法和使用方法嗎?

3 个解决方案

#1


1  

Well color me surprised. PostSharp lets you do exactly what you're looking for. In a nutshell, you'd use the ComponentInternalAttribute to control visibility of a type:

好顏色我很驚訝。 PostSharp可以讓您完全按照自己的意願行事。簡而言之,您將使用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(...) {
   }
   ...
}

#2


3  

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. I'd encourage you to expand your sample or rethink your design.

這絕對是@Habib提到的代碼味道(有人可以鏈接到特定的嗎?),但沒有更完整的例子,很難提供超出評論中已建議的替代品。我鼓勵您擴展您的樣本或重新考慮您的設計。


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. If warnings as errors is enabled (for all warnings or just this one), then it will be a compile error as you originally wanted.

這樣,每當有人試圖在代碼中的其他地方創建自己的項目時,他們將獲得一個2級CS0618警告,表明他們不應該使用該方法(請注意我沒有說不能)屬性。如果啟用了警告錯誤(對於所有警告或僅此警告),那么它將是您最初想要的編譯錯誤。

Be aware, nothing prevents others from adding these pragma statements to get around the error. However, with this method the developer can't say they didn't know they weren't supposed to use the constructor.

請注意,沒有什么可以阻止其他人添加這些pragma語句來解決錯誤。但是,使用這種方法,開發人員不能說他們不知道他們不應該使用構造函數。

#3


0  

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類的構造標記為private,並向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. Then you class is visible from other assemblies, but it cannot be directly instantiated, so forces the code user to use provided factory.

另一種方法是將Item類移動到另一個程序集,將其構造函數標記為internal,並實現另一個類(工廠),該類負責創建不同的Item對象。然后,您可以從其他程序集中看到該類,但它無法直接實例化,因此強制代碼用戶使用提供的工廠。


注意!

本站翻译的文章,版权归属于本站,未经许可禁止转摘,转摘请注明本文地址:https://www.itdaan.com/blog/2015/06/03/72a2f6f01c3617409b8909217c401955.html



 
粤ICP备14056181号  © 2014-2020 ITdaan.com