如何将对嵌套类成员的访问限制为封闭类?

是否可以指定嵌套类的成员可以被封闭类访问,但不能被其他类访问?

下面是这个问题的一个例子(当然,我的实际代码要复杂一些... ...) :

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; }
}


// ...
}

我想阻止客户端代码创建 JournalEntry的实例,但是 Journal必须能够创建它们。如果我将构造函数公开,任何人都可以创建实例... 但是如果我将其私有化,Journal将无法创建实例!

请注意,JournalEntry类必须是公共的,因为我希望能够向客户机代码公开现有条目。

如有任何建议,我将不胜感激!


更新: 感谢大家的投入,我最终使用了公共 IJournalEntry接口,由一个私有 JournalEntry类实现(尽管我的问题中有最后一个要求...)

25206 次浏览

在这种情况下,你可以:

  1. 使构造函数内部化-这将阻止该程序集外部的那些创建新实例或..。
  2. 重构 JournalEntry类以使用公共接口,并使实际的 JournalEntry类成为私有的或内部的。然后可以在隐藏实际实现的同时为集合公开接口。

我上面提到的内部作为一个有效的修饰符,但是根据你的要求,私人可能是更好的选择。

编辑: 对不起,我提到了私有构造函数,但是您已经在您的问题中处理了这一点。很抱歉我没有正确阅读!

如果你的类不是太复杂,你可以使用一个公开可见的接口并且使实际的实现类私有,或者你可以为 JornalEntry类创建一个受保护的构造函数并且使用一个公共构造函数从 JornalEntry派生出一个私有类 JornalEntryInstance,这个公共构造函数实际上是由你的 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);
}
}

如果您的实际类太复杂,以至于无法完成这两件事,而且您可以避免构造函数不完全不可见,那么您可以将构造函数设置为内部的,以便它只能在程序集中可见。

如果这也是不可行的,那么您可以将构造函数设置为私有,并使用反射从您的日志类调用它:

typeof(object).GetConstructor(new Type[] { }).Invoke(new Object[] { value });

现在我考虑一下,另一种可能性是在包含类中使用私有委托,该委托是从内部类设置的

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);
}
}

这应该能够提供您想要的可见性级别,而无需依赖于缓慢的反射或引入额外的类/接口

JournalEntry设置为 私有嵌套类型私有嵌套类型。任何公共成员只对封闭类型可见。

public class Journal
{
private class JournalEntry
{
}
}

如果需要让其他类可以使用 JournalEntry对象,可以通过公共接口公开它们:

public interface IJournalEntry
{
}


public class Journal
{
public IEnumerable<IJournalEntry> Entries
{
get { ... }
}


private class JournalEntry : IJournalEntry
{
}
}

实际上,这个问题有一个完整而简单的解决方案,不需要修改客户端代码或创建接口。

在大多数情况下,这个解决方案实际上比基于接口的解决方案更快,而且更容易编码。

public class Journal
{
private static Func<object, JournalEntry> _newJournalEntry;


public class JournalEntry
{
static JournalEntry()
{
_newJournalEntry = value => new JournalEntry(value);
}
private JournalEntry(object value)
{
...

一个更简单的方法是只使用一个 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);
}
}

对于泛型嵌套类 =)

我知道这是一个老问题,它已经有了一个公认的答案,然而,对于那些谷歌游泳谁可能有一个类似的情况下矿这个答案可能会提供一些帮助。

我遇到了这个问题,因为我需要实现与 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);
}
}

我们有 作为我们的内部嵌套类与私有构造函数,我们维护在我们的父类字典与这些通用工厂利用 充满活力的优势。因此,每次调用 TestMe伙计时,Foo 都会搜索是否已经创建了 T 的工厂,如果没有,那么它将创建调用嵌套类的静态构造函数。

测试:

private static void Main()
{
var foo = new Foo();


foo.TestMeDude<string>();
foo.TestMeDude<int>();
foo.TestMeDude<Foo>();


foo.TestMeDude<string>();


Console.ReadLine();
}

输出结果是:

enter image description here

Grizzly 提出的解决方案确实使在其他地方创建嵌套类变得有点困难,但并非不可能,就像 Tim Pohlmann 写的那样,有人仍然可以继承它并使用继承的类 ctor。

我利用了嵌套类可以访问容器私有属性这一事实,因此容器很好地发出了请求,而嵌套类提供了对 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
}


}
}

这个解决方案不是我平时工作时会做的事情,不是因为它会在其他地方导致问题,而是因为它是非传统的(我以前从未见过) ,所以它可能会给其他开发人员带来痛苦。