2009-11-03 66 views
43

是否可以指定一个嵌套类的成员可以通过封装类访问,而不是其他类?如何限制对嵌套类成员的访问以封闭类?

这里的问题的解释(当然我的实际代码是更复杂一点...):

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类实现(尽管我的问题最后的要求...)

+1

你可以使JournalEntry的(对象)构造 '内部';这会阻止其他程序集实例化日记条目,但同一程序集中的其他类仍然可以创建它们;但是如果你是组件的作者,这可能就足够了。 – 2009-11-03 02:24:00

+0

是的,我想到了这一点,但我宁愿无法在同一个程序集中创建实例......无论如何,谢谢! – 2009-11-03 03:17:33

回答

33

如果你的类是不是太复杂,你既可以使用一个接口是公开可见的,使实际实现类私有或者你可以做一个受保护的构造为JornalEntry类和具有JornalEntry与实际上是由您的Journal实例化的公共构造派生的私有类JornalEntryInstance

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

这应该给你,而无需诉诸慢反射或引入额外的类/接口

您所需的可见度
+0

界面是一个很好的解决方案,不知道为什么我没有想到它......谢谢! – 2009-11-03 03:22:31

+3

第一种方法有一个很大的缺点:其他类也可以继承自JournalEntry,然后调用构造函数。 – 2015-08-31 12:16:54

2

在这种情况下,你既可以:

  1. 使构造函数内部 - 这将停止与本集之外创建新实例或...
  2. 重构JournalEntry类使用公共界面,并使私人或内部的实际JournalEntry类。然后可以将该接口暴露给集合,同时隐藏实际的实现。

作为一个有效的修饰符,我在上面提到过,但根据您的要求,私有可能是更合适的选择。

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

1

我会使JournalEntry的构造函数内部:

public class Journal 
{ 
    public class JournalEntry 
    { 
     internal JournalEntry(object value) 
     { 
      this.Timestamp = DateTime.Now; 
      this.Value = value; 
     } 

     public DateTime Timestamp { get; private set; } 
     public object Value { get; private set; } 
    } 

    // ... 
} 
15

品牌JournalEntry a 私人嵌套类型。任何公共成员只能在封闭类型中看到。

public class Journal 
{ 
    private class JournalEntry 
    { 
    } 
} 

如果您需要提供给其他类JournalEntry对象,通过公共接口揭露他们:

public interface IJournalEntry 
{ 
} 

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

    private class JournalEntry : IJournalEntry 
    { 
    } 
} 
64

其实,有这个问题,不涉及修改完善和简单的解决方案客户端代码或创建一个接口。

对于大多数情况,此解决方案实际上比基于接口的解决方案更快,并且更易于编码。

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

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

好的和原创的方法...我必须记住那一个。谢谢 ! – 2009-11-03 13:08:56

+0

这是非常好的,一个宝贵的代码! – 2013-03-03 08:02:34

+2

Spiffy。被接受的答案具有相同的总体思路,但它是一堆其他选择的底部,所以看看这里! – aggieNick02 2013-04-22 15:54:51

10

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

好主意!我会记住这个诀窍,以防万一...... – 2013-05-02 15:04:29

0

对于一般嵌套类=)

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

我遇到了这个问题,因为我需要实现与OP相同的功能。对于我的第一个场景thisthis答案工作得很好。尽管如此,我还需要公开一个嵌套的泛型类。问题是你不能公开委托类型字段(工厂字段)与打开的泛型参数,而没有使自己的类通用,但显然这不是我们想要的,所以,这里是我的解决方案,这种情况:

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

我们有我们与私有构造内部嵌套类,我们十个分量在我们的父类字典,这些通用的工厂采取的动态优势。因此,每次调用TestMeDude时,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