如何避免在属性getter中进行递归调用?这是我的简单代码
public class UploadAttribute : Attribute
{
private Type _resourceType;
private string _select;
private string _change;
private string _remove;
public Type ResourceType
{
get { return _resourceType; }
set { _resourceType = value; }
}
public string Select
{
get { return GetResourceText(m => m.Select, "Select..."); }
set { _select = value; }
}
public string Change
{
get { return GetResourceText(m => m.Change, "Change..."); }
set { _change = value; }
}
public string Remove
{
get { return GetResourceText(m => m.Remove, "Remove"); }
set { _remove = value; }
}
private string GetResourceText(Expression<Func<UploadAttribute, string>> expression, string @default)
{
var value = expression.Compile().Invoke(this); // here .net is creating new UploadAttribute instance and use it for expression fnc
var result = value ?? @default;
if (_resourceType != null && !string.IsNullOrEmpty(value))
{
ResourceManager rm = new ResourceManager(_resourceType);
try
{
result = rm.GetString(value);
}
catch
{
// if string wasn't found in resource file than use what user specify; don't by big brother.
}
}
return result;
}
}
但是如果你看一下方法GetResourceText,我需要编译和调用表达式来获取给定属性的值。不幸的是,这个操作创建了UploadAttribute的新实例。在那一刻.net通过所有属性并调用getter,如果我没有弄错,并且在getter .net中编译并调用表达式来获取值或给定属性,一次又一次地反复到StackOverlowException。 您能否告诉我如何避免这种行为,但这个解决方案的简单性呢?
编辑:此类的一个部分是为按钮提供标题 - 用户可以设置哪些资源管理器使用多语言标题。 在上面的示例中,按钮选择从资源翻译,对于更改按钮,使用默认文本&#34;更改...&#34;并删除按钮标题&#34;销毁这个@&amp;#!&#34;。因此,如果用户没有指定属性值,则应用程序使用默认文本,否则尝试在资源中查找文本,如果找到匹配,则使用来自资源的文本,否则使用用户设置。
[Required]
[Upload(ResourceType = typeof(Resource), Select = "UploadSelect", Remove = "Destroy this @&#!")]
public HttpPostedFileBase Logo { get; set; }
答案 0 :(得分:2)
如果没有明确设置,您尝试实现的目的似乎是初始化这些属性。你这样做的方式是行不通的。
m => m.Remove
类型表达式将导致在无限递归中再次调用属性getter,直到发生堆栈溢出。
您可以使用lazy
构造,如下图所示。它的工作原理如下:
请注意,属性属性值的这种双重用途会导致相当脆弱的设计解决方案。如果找不到具有键“UploadSelect”的资源,那么它将成为按钮上的标题。
public class UploadAttribute : Attribute
{
private static readonly string kSelectDefaultCaption = "Select...";
private static readonly string kChangeDefaultCaption = "Change...";
private static readonly string kRemoveDefaultCaption = "Remove...";
private Type _resourceType;
private Lazy<string> _select = new Lazy<string>(() => kSelectDefaultCaption);
private Lazy<string> _change = new Lazy<string>(() => kChangeDefaultCaption);
private Lazy<string> _remove = new Lazy<string>(() => kRemoveDefaultCaption);
public Type ResourceType
{
get { return _resourceType; }
set { _resourceType = value; }
}
public string Select
{
get { return _select.Value; }
set { _select = new Lazy<string>(() => GetResourceText(value, kSelectDefaultCaption)); }
}
public string Change
{
get { return _change.Value; }
set { _change = new Lazy<string>(() => GetResourceText(value, kChangeDefaultCaption)); }
}
public string Remove
{
get { return _remove.Value; }
set { _remove = new Lazy<string>(() => GetResourceText(value, kRemoveDefaultCaption)); }
}
private string GetResourceText(string key, string @default)
{
// initialize to default.
var result = @default;
if (_resourceType != null && !string.IsNullOrEmpty(key))
{
// initialize to the value of the key,
// that could be a user supplied string literal
result = key;
// attempt to retrieve it from the resources.
ResourceManager rm = new ResourceManager(_resourceType);
try
{
result = rm.GetString(key);
}
catch
{
// could not retrieve key, using the key value as the result.
}
}
return result;
}
}
答案 1 :(得分:1)
我非常倾倒。它根本不会创建新的实例;我问的那一刻,我回答了我的问题。
return GetResourceText(m => m.Select, "Select...");
是递归但没有结束,但return GetResourceText(m => m._select, "Select...");
不是,因为我没有再次调用方法GetResourceText。
对不起男孩们的愚蠢问题。
答案 2 :(得分:1)
如果您希望在使用属性名称时保持编译时的安全性,请稍微修改Alex的答案:
public class UploadAttribute : Attribute
{
private Type _resourceType;
private Lazy<string> _select;
private Lazy<string> _change;
private Lazy<string> _remove;
UploadAttribute()
{
_select = new Lazy<string>(() => GetResourceText(m => m.Select, "Select..."));
_change = new Lazy<string>(() => GetResourceText(m => m.Change, "Change..."));
_remove = new Lazy<string>(() => GetResourceText(m => m.Remove, "Remove..."));
}
public Type ResourceType
{
get { return _resourceType; }
set { _resourceType = value; }
}
public string Select
{
get { return _select.Value; }
set { _select = new Lazy<string>(() => value); }
}
public string Change
{
get { return _change.Value; }
set { _change = new Lazy<string>(() => value); }
}
public string Remove
{
get { return _remove.Value; }
set { _remove = new Lazy<string>(() => value); }
}
private string GetResourceText(Expression<Func<UploadAttribute, string>> expression, string @default)
{
var result = @default;
var memberExpression = expression.Body as MemberExpression;
if (_resourceType != null && memberExpression != null)
{
ResourceManager rm = new ResourceManager(_resourceType);
try
{
result = rm.GetString(memberExpression.Member.Name);
}
catch
{
// if string wasn't found in resource file than use what user specify; don't by big brother.
}
}
return result;
}
}