是否可以指定封闭类可以访问嵌套类的成员,而不能指定其他类?
以下是问题的说明(当然我的实际代码有点复杂......):
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
课程实施(尽管我的问题中有最后一项要求......)
答案 0 :(得分:76)
实际上,这个问题有一个完整而简单的解决方案,不涉及修改客户端代码或创建接口。
对于大多数情况,此解决方案实际上比基于接口的解决方案更快,并且更容易编码。
public class Journal
{
private static Func<object, JournalEntry> _newJournalEntry;
public class JournalEntry
{
static JournalEntry()
{
_newJournalEntry = value => new JournalEntry(value);
}
private JournalEntry(object value)
{
...
答案 1 :(得分:41)
如果您的类不是太复杂,您可以使用公开可见的接口并将实际的实现类设为私有,或者您可以为JornalEntry
类创建受保护的构造函数并拥有私有类{{1从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);
}
}
现在我考虑一下,另一种可能性是在内容类中设置的包含类中使用私有委托
typeof(object).GetConstructor(new Type[] { }).Invoke(new Object[] { value });
这可以为您提供所需的可见性级别,而无需采用慢速反射或引入其他类/接口
答案 2 :(得分:20)
将JournalEntry
设为私有嵌套类型。任何公共成员只对封闭类型可见。
public class Journal
{
private class JournalEntry
{
}
}
如果您需要将JournalEntry
个对象用于其他类,请通过公共接口公开它们:
public interface IJournalEntry
{
}
public class Journal
{
public IEnumerable<IJournalEntry> Entries
{
get { ... }
}
private class JournalEntry : IJournalEntry
{
}
}
答案 3 :(得分:12)
更简单的方法是使用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);
}
}
答案 4 :(得分:3)
在这种情况下,您可以:
JournalEntry
类以使用公共接口,并将实际的JournalEntry
类设为私有或内部。然后,可以在隐藏实际实现的同时公开接口以进行收集。我上面提到内部是一个有效的修饰符,但是根据你的要求,private可能是更适合的选择。
编辑:抱歉,我提到了私有构造函数,但您已经在问题中处理了这一点。我为没有正确阅读而道歉!
答案 5 :(得分:0)
对于通用嵌套类=)
我知道这是一个老问题,它已经被接受了答案,但对于那些可能有类似场景的谷歌游泳运动员来说,这个答案可能会提供一些帮助。
我遇到了这个问题,因为我需要实现与OP相同的功能。对于我的第一个方案this和this,答案工作得很好。不过我还需要公开一个嵌套的泛型类。问题是您不能使用打开的通用参数公开委托类型字段(工厂字段)而不使您自己的类通用,但显然这不是我们想要的,所以,这是我的解决方案:
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);
}
}
我们有 Boo 作为我们的私有构造函数的内部嵌套类,我们在父类中使用动态这些通用工厂的字典。因此,每次调用 TestMeDude 时,Foo都会搜索是否已经创建了T的工厂,如果没有,则会创建它调用嵌套类&#39;静态构造函数。
测试:
private static void Main()
{
var foo = new Foo();
foo.TestMeDude<string>();
foo.TestMeDude<int>();
foo.TestMeDude<Foo>();
foo.TestMeDude<string>();
Console.ReadLine();
}
输出结果为:
答案 6 :(得分:0)
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
}
}
}
这种解决方案不是我在日常工作中会做的事情,不是因为它会在其他地方导致问题,而是因为它是非常规的(我从未见过),因此可能会使其他开发人员感到痛苦。