必要なのは、あるクラスのプロパティを他の 1 つのクラス (一種のマネージャー クラス) からのみ「設定可能」にする方法だけです。
これはC#でも可能ですか?
私の同僚は、私に設計上の欠陥があることを「確実に」知らせてきましたが、私が敗北を認める前に、少なくともコミュニティに質問する必要があると感じています!
必要なのは、あるクラスのプロパティを他の 1 つのクラス (一種のマネージャー クラス) からのみ「設定可能」にする方法だけです。
これはC#でも可能ですか?
私の同僚は、私に設計上の欠陥があることを「確実に」知らせてきましたが、私が敗北を認める前に、少なくともコミュニティに質問する必要があると感じています!
いいえ、C# でクリーンな方法でこれを行うことは実際には不可能です。おそらく設計上の欠陥があります;-)
修飾子を使用するinternal
と、同じアセンブリ内のすべての型がデータにアクセスできます (または、指定されたアセンブリを使用する場合は指定されたアセンブリにアクセスできますが、C# には同等のもの[InternalsVisibleTo]
はありません。friend
例えば:
public string Foo {get; internal set;}
設計上の欠陥があります。また、データの隠蔽について妄想しないでください。これを行う3.5の方法は次のとおりです。
class Program
{
static void Main(string[] args)
{
Managed m = new Managed();
Console.WriteLine(m.PrivateSetter);
m.Mgr.SetProperty("lol");
Console.WriteLine(m.PrivateSetter);
Console.Read();
}
}
public class Managed
{
private Manager _mgr;
public Manager Mgr
{
get { return _mgr ?? (_mgr = new Manager(s => PrivateSetter = s)); }
}
public string PrivateSetter { get; private set; }
public Managed()
{
PrivateSetter = "Unset";
}
}
public class Manager
{
private Action<string> _setPrivateProperty;
public Manager(Action<string> setter)
{
_setPrivateProperty = setter;
}
public void SetProperty(string value)
{
_setPrivateProperty(value);
}
}
ラムダ以前の日にそれを行う方法は次のとおりです。
public class Managed
{
private Manager _mgr;
public Manager Mgr
{
get { return _mgr ?? (_mgr = new Manager(this)); }
}
public string PrivateSetter { get; private set; }
public Managed()
{
PrivateSetter = "Unset";
}
public class Manager
{
public void SetProperty(string value)
{
m.PrivateSetter = value;
}
private Managed m;
public Manager(Managed man)
{
m = man;
}
}
}
それを行う最良の方法は次のとおりです。
/// <summary>
/// Gets or sets foo
/// <b>Setter should only be invoked by SomeClass</b>
/// </summary>
public Object Foo
{
get { return foo; }
set { foo = value; }
}
複雑なアクセスまたは継承の制限があり、それを適用するにはコードが複雑すぎる場合、適切にコメントすることが最善の方法である場合があります。
ただし、このコードを使用する開発者の善意に依存しているため、この制限がセキュリティに影響を与える場合は、これに依存できないことに注意してください。
その方法ではできませんが、派生クラスからプロパティの setter メソッドにアクセスできるため、目的に継承を使用できます。保護されたアクセス修飾子を配置するだけです。そうしようとするなら、あなたの同僚は正しいです:)。次のようにして試すことができます。
public string Name
{
get{ return _name; }
protected set { _name = value; }
}
プロパティの set メソッドは、派生クラスからのみアクセスできることに注意してください。
または、これら 2 つのクラスをアセンブリに単独で配置し、セッターを内部として使用することもできます。ただし、milotによる以前の回答(継承および保護)が意味をなさない限り、設計上の欠陥に賛成票を投じます。
あなたがすることができます:
public void setMyProperty(int value, Object caller)
{
if(caller is MyManagerClass)
{
MyProperty = value;
}
}
これは、呼び出し元のクラスから「this」ポインターを使用できることを意味します。あなたが達成しようとしていることの論理に疑問を呈しますが、シナリオを知らなければ、これ以上アドバイスすることはできません. 私が言いたいのは、コードをリファクタリングしてより明確にすることが可能であれば、そうする価値があるということです。
しかし、これは非常に厄介で、絶対に確実ではありません...警告されています!
代わりに...
プロパティを持つクラス (クラス A) からマネージャー クラス (クラス B) にデリゲートを渡すことができます。デリゲートは、A 内のプライベート関数を参照して、B がそのデリゲートを通常の関数として呼び出すことができるようにします。これにより、A が B について知っている可能性がなくなり、A が B の前に作成される可能性がなくなります。
リフレクション、ただし、アクセス修飾子を回避するためだけにこれを実行する必要があるのは、おそらく設計が悪いことを示していることに同意します。
public class Widget
{
private int count;
public int Count
{
get { return this.count; }
private set { this.count = value; }
}
}
public static class WidgetManager
{
public static void CatastrophicErrorResetWidgetCount( Widget widget )
{
Type type = widget.GetType();
PropertyInfo info = type.GetProperty("Count",BindingFlags.Instance|BindingFlags.NonPublic);
info.SetValue(widget,0,null);
}
}
あなたの目標がクラスFoo
にいくつかのプロパティ(例えばBar
、タイプのBiz
)を他のオブジェクトによって変更させ、それを公に公開することなく変更させることである場合、それを行う簡単な方法は、Foo
いくつかによって変更可能であると思われるインスタンスを持つことです。 other オブジェクトを渡して、渡された値Action<Biz>
に変更されるプライベート メソッドを指す他のオブジェクトを渡しBar
ます。他のオブジェクトは、そのデリゲートを使用して、Bar
それを提供したオブジェクトの値を変更できます。
インスタンスごとにそのような機能を公開するのではなく、ある型のすべてのインスタンスにの任意のインスタンスの値Woozle
を設定する機能を与えたい場合は、型のパラメーターを取り、型とタイプの 1 つ。 次に、を受け取り、それをとともに渡す静的メソッドが必要です。のクラス初期化子は new を生成し、それを;に渡す必要があります。オブジェクトをデリゲートとともに渡します。 Bar
Foo
Woozle
Woozle.InstallFooBarSetter
Action<Foo, Biz>
Object
Foo
WoozleRequestBarSetter
Object
Woozle.InstallFooBarSetter
Action<Foo,Biz>
Woozle
Object
Foo.RequestBarSetter
Woozle.InstallFooBarSetter
Woozle
次に、渡されたオブジェクトが生成したものであることを確認し、生成したものである場合は、適切なデリゲートをインストールします。このようにすることで、誰もWoozle
デリゲートを取得できないことが保証され (デリゲートは にのみ渡されるためWoozle.InstallFooBarSetter
)、Woozle
そのデリゲートが確実に取得されるようになります(作成Foo
したオブジェクトに他の誰もアクセスできず、何もしないため)。それなしで)。Woozle
Woozle.InstallFooBarSetter
あなたが探しているのは、C++ が Friend クラスを呼び出すものですが、c# にも vb にもこの機能はありません。このような機能のメリットについては、多くの議論があります。これは、クラス間の非常に強い結合を促進するためです。これを c# で実装できる唯一の方法は、リフレクションを使用することです。
これが設計上の欠陥である理由は、2 つのオブジェクトのスコープが混同されているように見えるためです。
クラスのプロパティは、少なくとも内部的には、そのクラスのコンテキストでアクセスできる必要があります。
item クラスの設定可能なプロパティは、実際には manager クラスのプロパティのようです。
2 つのクラスを密接に結合することで、必要なことと同様のことを行うことができます。
public class MyItem {
internal MyItemManager manager { get;set; }
public string Property1 {
get { return manager.GetPropertyForItem( this ); }
}
}
残念ながら、これも素晴らしいデザインではありません。
保護されたプロパティを持つ実際のクラスから継承する「設定可能なクラス」にパブリック プロパティを作成することで、これを実現できます。この方法では、継承クラスのみが設定でき、継承しないクラスは設定できません。しかし、欠点は、継承クラスが必要になることです...
それが設計上の欠陥であるかどうかは、何をしたいかによって異なります。System.Diagnostics の StackTrace クラスを使用して、プロパティを設定するクラスの Type を取得し、プロパティの設定を許可するタイプと比較することができます。