C# 如何限制对嵌套类成员的访问以封闭类?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1664793/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to restrict access to nested class member to enclosing class?
提问by Thomas Levesque
Is it possible to specify that members of a nested class can be accessed by the enclosing class, but not other classes ?
是否可以指定封闭类可以访问嵌套类的成员,但其他类不能访问?
Here's an illustration of the problem (of course my actual code is a bit more complex...) :
这是问题的说明(当然我的实际代码有点复杂......):
public class Journal
{
public class JournalEntry
{
public JournalEntry(object value)
{
this.Timestamp = DateTime.Now;
this.Value = value;
}
public DateTime Timestamp { get; private set; }
public object Value { get; private set; }
}
// ...
}
I would like to prevent client code from creating instances of JournalEntry
, but Journal
must be able to create them. If I make the constructor public, anyone can create instances... but if I make it private, Journal
won't be able to !
我想阻止客户端代码创建 的实例JournalEntry
,但Journal
必须能够创建它们。如果我将构造函数Journal
设为公开,任何人都可以创建实例……但如果我将其设为私有,就不能!
Note that the JournalEntry
class must be public, because I want to be able to expose existing entries to client code.
请注意,JournalEntry
该类必须是公共的,因为我希望能够将现有条目公开给客户端代码。
Any suggestion would be appreciated !
任何建议将不胜感激!
UPDATE: Thanks everyone for your input, I eventually went for the public IJournalEntry
interface, implemented by a private JournalEntry
class (despite the last requirement in my question...)
更新:感谢大家的投入,我最终选择了IJournalEntry
由私有JournalEntry
类实现的公共接口(尽管我的问题中有最后一个要求......)
采纳答案by Grizzly
If your class is not too complex you could either use an interface which is publically visible and make the actual implementing class private or you could make a protected constructor for the JornalEntry
class and have a private class JornalEntryInstance
derived from JornalEntry
with a public constructor which is actually instantiated by your Journal
.
如果您的类不是太复杂,您可以使用公开可见的接口并使实际实现类成为私有,或者您可以为JornalEntry
该类创建一个受保护的构造函数并JornalEntryInstance
从JornalEntry
一个公共构造函数派生一个私有类,该类实际上由你的Journal
。
public class Journal
{
public class JournalEntry
{
protected JournalEntry(object value)
{
this.Timestamp = DateTime.Now;
this.Value = value;
}
public DateTime Timestamp { get; private set; }
public object Value { get; private set; }
}
private class JournalEntryInstance: JournalEntry
{
public JournalEntryInstance(object value): base(value)
{ }
}
JournalEntry CreateEntry(object value)
{
return new JournalEntryInstance(value);
}
}
If your actual class is too complex to do either of that and you can get away with the constructor being not completely invisible, you can make the constructor internal so it is only visible in the assembly.
如果您的实际类太复杂而无法执行其中任何一个,并且您可以避免构造函数不是完全不可见,则可以将构造函数设为内部,以便它仅在程序集中可见。
If that too is infeasible you can always make the constructor private and use reflection to call it from your journal class:
如果这也不可行,您始终可以将构造函数设为私有并使用反射从您的日志类中调用它:
typeof(object).GetConstructor(new Type[] { }).Invoke(new Object[] { value });
Now that I think about it, another possibility would use a private delegate in the containing class which is set from the inner class
现在我考虑了一下,另一种可能性是在包含类中使用私有委托,该委托是从内部类设置的
public class Journal
{
private static Func<object, JournalEntry> EntryFactory;
public class JournalEntry
{
internal static void Initialize()
{
Journal.EntryFactory = CreateEntry;
}
private static JournalEntry CreateEntry(object value)
{
return new JournalEntry(value);
}
private JournalEntry(object value)
{
this.Timestamp = DateTime.Now;
this.Value = value;
}
public DateTime Timestamp { get; private set; }
public object Value { get; private set; }
}
static Journal()
{
JournalEntry.Initialize();
}
static JournalEntry CreateEntry(object value)
{
return EntryFactory(value);
}
}
This should give you your desired visibility levels without needing to resort on slow reflection or introducing additional classes / interfaces
这应该为您提供所需的可见性级别,而无需诉诸缓慢反射或引入额外的类/接口
回答by Paul Mason
In this case you could either:
在这种情况下,您可以:
- Make the constructor internal - this stops those outside this assembly creating new instances or...
- Refactor the
JournalEntry
class to use a public interface and make the actualJournalEntry
class private or internal. The interface can then be exposed for collections while the actual implementation is hidden.
- 将构造函数设为内部 - 这会阻止该程序集外部的构造函数创建新实例或...
- 重构
JournalEntry
类以使用公共接口并将实际类设为JournalEntry
私有或内部。然后可以为集合公开接口,而隐藏实际的实现。
I mentioned internal as a valid modifier above however depending on your requirements, private may be the better suited alternative.
我在上面提到 internal 作为有效修饰符,但是根据您的要求,private 可能是更合适的替代方案。
Edit:Sorry I mentioned private constructor but you've already dealt with this point in your question. My apologies for not reading it correctly!
编辑:抱歉,我提到了私有构造函数,但您已经在问题中处理了这一点。我为没有正确阅读而道歉!
回答by Sam Harwell
Make JournalEntry
a private nested type. Any public members will be visible only to the enclosing type.
制作JournalEntry
一个私有嵌套类型。任何公共成员仅对封闭类型可见。
public class Journal
{
private class JournalEntry
{
}
}
If you need to make JournalEntry
objects available to other classes, expose them via a public interface:
如果您需要将JournalEntry
对象提供给其他类,请通过公共接口公开它们:
public interface IJournalEntry
{
}
public class Journal
{
public IEnumerable<IJournalEntry> Entries
{
get { ... }
}
private class JournalEntry : IJournalEntry
{
}
}
回答by Ray Burns
Actually there is a complete and simple solution to this problem that doesn't involve modifying the client code or creating an interface.
其实这个问题有一个完整而简单的解决方案,不需要修改客户端代码或创建接口。
This solution is actually faster than the interface-based solution for most cases, and easier to code.
在大多数情况下,此解决方案实际上比基于接口的解决方案更快,并且更易于编码。
public class Journal
{
private static Func<object, JournalEntry> _newJournalEntry;
public class JournalEntry
{
static JournalEntry()
{
_newJournalEntry = value => new JournalEntry(value);
}
private JournalEntry(object value)
{
...
回答by Marc Gravell
A simpler approach is to just use an internal
constructor, but make the caller prove who they are by supplying a reference that only the legitimate callercould know (we don't need to be concerned about non-public reflection, because if the caller has access to non-public reflection then we've already lost the fight - they can access a private
constructor directly); for example:
一个更简单的方法是只使用一个internal
构造函数,但是通过提供一个只有合法调用者才能知道的引用来让调用者证明他们是谁(我们不需要关心非公共反射,因为如果调用者有权访问到非公共反射,那么我们已经输了——他们可以private
直接访问构造函数);例如:
class Outer {
// don't pass this reference outside of Outer
private static readonly object token = new object();
public sealed class Inner {
// .ctor demands proof of who the caller is
internal Inner(object token) {
if (token != Outer.token) {
throw new InvalidOperationException(
"Seriously, don't do that! Or I'll tell!");
}
// ...
}
}
// the outer-class is allowed to create instances...
private static Inner Create() {
return new Inner(token);
}
}
回答by taquion
For generic nested class =)
对于通用嵌套类 =)
I know this is an old question and it has already an accepted answer, nevertheless for those google swimmers who may have a similar scenario to mine this answer may provide some help.
我知道这是一个老问题,它已经得到了一个公认的答案,但是对于那些可能有类似情况来挖掘这个答案的谷歌游泳运动员来说,这个答案可能会提供一些帮助。
I came across this question for I needed to implement the same feature as the OP. For my first scenario thisand thisanswers worked just fine. Nevertheless I needed also to expose a nested generic class. The problem is that you can not expose a delegate type field (the factory field) with opened generic parameters without making your own class generic, but obviously this is not what we want, so, here is my solution for such scenario:
我遇到了这个问题,因为我需要实现与 OP 相同的功能。对于我的第一个场景,这个和这个答案工作得很好。尽管如此,我还需要公开一个嵌套的泛型类。问题是你不能在不使你自己的类通用的情况下公开具有打开泛型参数的委托类型字段(工厂字段),但显然这不是我们想要的,所以,这是我针对这种情况的解决方案:
public class Foo
{
private static readonly Dictionary<Type, dynamic> _factories = new Dictionary<Type, dynamic>();
private static void AddFactory<T>(Func<Boo<T>> factory)
=> _factories[typeof(T)] = factory;
public void TestMeDude<T>()
{
if (!_factories.TryGetValue(typeof(T), out var factory))
{
Console.WriteLine("Creating factory");
RuntimeHelpers.RunClassConstructor(typeof(Boo<T>).TypeHandle);
factory = _factories[typeof(T)];
}
else
{
Console.WriteLine("Factory previously created");
}
var boo = (Boo<T>)factory();
boo.ToBeSure();
}
public class Boo<T>
{
static Boo() => AddFactory(() => new Boo<T>());
private Boo() { }
public void ToBeSure() => Console.WriteLine(typeof(T).Name);
}
}
We have Booas our internal nested class with a private constructor and we mantain on our parent class a dictionary with these generic factories taking advantage of dynamic. So, each time TestMeDudeis called, Foo searches for whether the factory for T has already been created, if not it creates it calling nested class' static constructor.
我们将Boo作为带有私有构造函数的内部嵌套类,并且我们在父类上维护一个字典,其中包含这些利用dynamic 的泛型工厂。因此,每次调用TestMeDude 时,Foo 都会搜索 T 的工厂是否已经创建,如果没有,则调用嵌套类的静态构造函数创建它。
Testing:
测试:
private static void Main()
{
var foo = new Foo();
foo.TestMeDude<string>();
foo.TestMeDude<int>();
foo.TestMeDude<Foo>();
foo.TestMeDude<string>();
Console.ReadLine();
}
The output is:
输出是:
回答by user1852503
The solution Grizzly suggested does make it a bit hard to create the nested class somewhere else but not impossible,like Tim Pohlmann wrote someone can still inherit it and use the inheriting class ctor.
Grizzly 建议的解决方案确实让在其他地方创建嵌套类变得有点困难,但并非不可能,就像 Tim Pohlmann 写的那样,有人仍然可以继承它并使用继承类 ctor。
I'm taking advantage of the fact that nested class can access the container private properties, so the container asks nicely and the nested class gives access to the ctor.
我正在利用嵌套类可以访问容器私有属性的事实,因此容器很好地询问并且嵌套类可以访问构造函数。
public class AllowedToEmailFunc
{
private static Func<long, EmailPermit> CreatePermit;
public class EmailPermit
{
public static void AllowIssuingPermits()
{
IssuegPermit = (long userId) =>
{
return new EmailPermit(userId);
};
}
public readonly long UserId;
private EmailPermit(long userId)
{
UserId = userId;
}
}
static AllowedToEmailFunc()
{
EmailPermit.AllowIssuingPermits();
}
public static bool AllowedToEmail(UserAndConf user)
{
var canEmail = true; /// code checking if we can email the user
if (canEmail)
{
return IssuegPermit(user.UserId);
}
else
{
return null
}
}
}
This solution is not something I would do on a regular day on the job, not because it will lead to problems in other places but because it's unconventional (I've never seen it before) so it might cause other developers pain .
这个解决方案不是我在日常工作中会做的事情,不是因为它会导致其他地方出现问题,而是因为它非常规(我以前从未见过)所以它可能会给其他开发人员带来痛苦。