중첩 클래스 멤버에 대한 액세스를 포함 클래스로 제한하는 방법은 무엇입니까?
중첩 클래스의 멤버가 포함 된 클래스에서 액세스 할 수 있지만 다른 클래스에서는 액세스 할 수 없도록 지정할 수 있습니까?
다음은 문제에 대한 설명입니다 (물론 실제 코드는 조금 더 복잡합니다 ...).
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
클래스로 구현 된 공개 인터페이스 로갔습니다 (제 질문의 마지막 요구 사항에도 불구하고 ...)
클래스가 너무 복잡하지 않은 경우 공개적으로 표시되는 인터페이스를 사용하여 실제 구현 클래스를 비공개로 만들거나 클래스에 대해 보호 된 생성자를 만들고 실제로 인스턴스화되는 공개 생성자로 파생 된 JornalEntry
비공개 클래스를 가질 수 있습니다. 귀하의 .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
클래스를 개인 또는 내부로 만듭니다. 그런 다음 실제 구현이 숨겨져있는 동안 인터페이스를 컬렉션에 대해 노출 할 수 있습니다.
위에서 유효한 수정 자로 내부를 언급했지만 요구 사항에 따라 private이 더 적합한 대안이 될 수 있습니다.
편집 : 미안하지만 개인 생성자를 언급했지만 이미 질문 에서이 점을 다루었습니다. 제대로 읽지 못해 죄송합니다!
일반 중첩 클래스 =)
나는 이것이 오래된 질문이라는 것을 알고 있으며 이미 받아 들여진 대답을 가지고 있지만이 대답과 비슷한 시나리오를 가지고있을 수있는 Google 수영 선수에게는 도움이 될 수 있습니다.
I came across this question for I needed to implement the same feature as the OP. For my first scenario this and this answers worked just fine. Nevertheless I needed also to expose a nested generic class. The problem is that you can not expose a delegate type field (the factory field) with opened generic parameters without making your own class generic, but obviously this is not what we want, so, here is my solution for such scenario:
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);
}
}
We have Boo as our internal nested class with a private constructor and we mantain on our parent class a dictionary with these generic factories taking advantage of dynamic. So, each time TestMeDude is called, Foo searches for whether the factory for T has already been created, if not it creates it calling nested class' static constructor.
Testing:
private static void Main()
{
var foo = new Foo();
foo.TestMeDude<string>();
foo.TestMeDude<int>();
foo.TestMeDude<Foo>();
foo.TestMeDude<string>();
Console.ReadLine();
}
The output is:
The solution Grizzly suggested does make it a bit hard to create the nested class somewhere else but not impossible,like Tim Pohlmann wrote someone can still inherit it and use the inheriting class ctor.
I'm taking advantage of the fact that nested class can access the container private properties, so the container asks nicely and the nested class gives access to the 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
}
}
}
This solution is not something I would do on a regular day on the job, not because it will lead to problems in other places but because it's unconventional (I've never seen it before) so it might cause other developers pain .
'program story' 카테고리의 다른 글
내 Mac에서 dex2jar을 사용할 수 없습니다 : 권한이 거부되었습니다. (0) | 2020.12.14 |
---|---|
사용할 것, int 또는 Integer (0) | 2020.12.14 |
현재 실행중인 자바 스크립트 코드의 파일 경로를 얻는 방법 (0) | 2020.12.14 |
C ++에서 변수, 메서드 등에 대한 좋은 명명 규칙은 무엇입니까? (0) | 2020.12.14 |
Rails 3 respond_to : 기본 형식? (0) | 2020.12.14 |