ネストされたクラスメンバーへのアクセスを、それを囲んでいるクラスに制限するにはどうすればよいですか?
-
12-09-2019 - |
質問
入れ子になったクラスのメンバーには、それを囲んでいるクラスからはアクセスできるが、他のクラスからはアクセスできないように指定することはできますか?
問題の図は次のとおりです (もちろん、実際のコードはもう少し複雑です...)。
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
既存のエントリをクライアント コードに公開できるようにしたいため、クラスは public である必要があります。
ご提案をお待ちしております。
アップデート:皆さんのご意見に感謝し、最終的には一般公開に踏み切りました IJournalEntry
インターフェイス、プライベートによって実装される JournalEntry
class (私の質問の最後の要件にもかかわらず...)
解決
あなたのクラスでは、公に表示されているインターフェースを使用して、実際の実装クラスはプライベート作ることができるのいずれか、あまりにも複雑ではありませんか、JornalEntry
クラスのprotectedコンストラクタを作成し、国民と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 });
今、私はそれについて考えることを、別の可能性は、内部クラス
から設定されている含まれているクラスでプライベートデリゲートを使用します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);
}
}
これはあなたの希望の可視性レベルを与える必要があり、低速反射に頼る必要または追加のクラス/インタフェースを導入することなく、
他のヒント
実際にクライアントのコードを変更するか、インターフェイスを作成含まない、この問題への完全かつ簡単な解決策があります。
このソリューションは、コードに実際にほとんどの場合のためのインターフェイスベースのソリューションよりも速く、かつ容易になります。
public class Journal
{
private static Func<object, JournalEntry> _newJournalEntry;
public class JournalEntry
{
static JournalEntry()
{
_newJournalEntry = value => new JournalEntry(value);
}
private JournalEntry(object value)
{
...
のJournalEntry
にのプライベートネストされたタイプを確認します。すべてのパブリックメンバーのみが囲む形に見えるようになります。
public class Journal
{
private class JournalEntry
{
}
}
あなたは他のクラスにJournalEntry
オブジェクトが利用できるようにする必要がある場合は、パブリックインターフェイスを介してそれらを公開:
public interface IJournalEntry
{
}
public class Journal
{
public IEnumerable<IJournalEntry> Entries
{
get { ... }
}
private class JournalEntry : IJournalEntry
{
}
}
簡単な方法は、ちょうど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);
}
}
この場合、次のいずれかを行うことができます。
- コンストラクターを内部にします - これにより、このアセンブリの外部にあるコンストラクターが新しいインスタンスを作成することがなくなります。
- リファクタリング
JournalEntry
クラスを使用してパブリックインターフェイスを使用し、実際のインターフェイスを作成しますJournalEntry
クラスプライベートまたは内部。その後、実際の実装は非表示にしながら、インターフェイスをコレクション用に公開できます。
上では有効な修飾子として Internal について説明しましたが、要件によっては、private の方が適切な代替手段になる場合があります。
編集: 申し訳ありませんが、プライベート コンストラクターについて言及しましたが、この点は質問ですでに扱っています。正しく読んでいなかったことをお詫びします。
の一般的な入れ子になったクラスの場合=)の
私は、これは古い質問ですけど、それにもかかわらず、いくつかの助けを提供することができるこの答えを採掘するために同様のシナリオを有していてもよく、それらのGoogleのスイマーのために、すでに受け入れ答えを持っています。
私は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);
}
}
私たちは持っているのプライベートコンストラクタを持つ当社の内部ネストされたクラスとしてのブーと私たちは私たちの親クラスに<強力/>のダイナミックを利用してこれらの一般的な工場と辞書をmantain。だから、毎回の 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();
}
出力されます:
グリズリーが提案された解決策は、それは少しどこか他のネストされたクラスを作成するのは難しいが、不可能ではない作るんます。
私は、ネストされたクラスは、コンテナのプライベートプロパティにアクセスすることができるという事実を利用していますので、コンテナがうまく尋ねると、ネストされたクラスは、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
}
}
}
<時間>
このソリューションは、それが他の場所での問題につながるではないので、私は、仕事上の正規の日にどうなるものではありませんが、それは型破りだから(私は前にそれを見たことがない)ので、それは他の開発者の痛みを引き起こす可能性があります。