2009-11-03 61 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