我所需要的只是一种使一个类的属性只能从另一个类(一种管理器类)“设置”的方法。
这在 C# 中甚至可能吗?
我的同事“可靠”地告诉我我有一个设计缺陷,但我觉得我至少应该在认输之前询问社区!
我所需要的只是一种使一个类的属性只能从另一个类(一种管理器类)“设置”的方法。
这在 C# 中甚至可能吗?
我的同事“可靠”地告诉我我有一个设计缺陷,但我觉得我至少应该在认输之前询问社区!
不,在 C# 中不可能以任何干净的方式做到这一点。您可能有设计缺陷;-)
您可以使用internal
修饰符,它允许同一程序集中的所有类型访问数据(或指定程序集,如果使用[InternalsVisibleTo]
- 但不是:C# 中没有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);
}
}
以下是我们在 lambda 之前的日子里是如何做到的:
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 方法只能从派生类访问。
或者您可以将这两个类单独放在一个程序集中,并将 setter 作为内部。不过,我会投票支持设计缺陷,除非 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);
}
}
这是一个设计缺陷的原因是因为它似乎混淆了两个对象的范围。
类的属性应该可以在该类的上下文中访问,至少在内部是可访问的。
听起来您的项目类上的可设置属性实际上是管理器类的属性。
您可以通过紧密耦合这两个类来做与您想要的类似的事情:
public class MyItem {
internal MyItemManager manager { get;set; }
public string Property1 {
get { return manager.GetPropertyForItem( this ); }
}
}
不幸的是,这也不是很好的设计。
您正在寻找的是 C++ 调用的 Friend 类,但 c# 或 vb 都没有此功能。关于这种功能的优点存在很多争论,因为它几乎鼓励类之间非常强的耦合。在 c# 中实现这一点的唯一方法是使用反射。
如果您的目标是让一个类Foo
让某个属性(例如Bar
type Biz
)被某个其他对象更改,而不公开地公开它,那么一种简单的方法是拥有一个Foo
应该可以被某些人更改的实例其他对象传递另一个对象,Action<Biz>
该对象指向一个私有方法,该方法更改Bar
为传入的值。另一个对象可以使用该委托来更改Bar
提供它的对象的值。
如果希望赋予某种类型Woozle
的所有实例设置 的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
不会做任何事情没有它)。
如果它是一个设计缺陷取决于你想要做什么。您可以使用 System.Diagnostics 中的 StackTrace 类来获取设置您的属性的类的类型,然后与您希望允许设置您的属性的类型进行比较..但也许有更好的方法来执行这样的事情(例如拳击)