资源文件中的C#属性文本?
我有一个属性,我想将文本从资源文件加载到该属性资源文件中的C#属性文本?,c#,resources,attributes,C#,Resources,Attributes,我有一个属性,我想将文本从资源文件加载到该属性 [IntegerValidation(1, 70, ErrorMessage = Data.Messages.Speed)] private int i_Speed; 但我一直在 属性参数必须是常量表达式、typeof表达式或属性参数类型的数组创建表达式 如果我添加一个字符串而不是Data.Messages.Text,它将非常有效,例如: [IntegerValidation(1, 70, ErrorMessage = "Invalid max
[IntegerValidation(1, 70, ErrorMessage = Data.Messages.Speed)]
private int i_Speed;
但我一直在
属性参数必须是常量表达式、typeof表达式或属性参数类型的数组创建表达式
如果我添加一个字符串而不是Data.Messages.Text,它将非常有效,例如:
[IntegerValidation(1, 70, ErrorMessage = "Invalid max speed")]
有什么想法吗?使用一个字符串作为资源的名称。NET对一些内部属性执行此操作。编译时,属性值将硬编码到程序集中。如果要在执行时执行任何操作,则需要使用常量作为键,然后将一些代码放入属性类本身以加载资源。属性的性质是,放入属性属性属性中的数据必须是常量。这些值将存储在程序集中,但决不会产生执行的编译代码。因此,您不能使用依赖于执行的属性值来计算结果。这是我的解决方案。我在属性中添加了resourceName和resourceType属性,就像microsoft在DataAnnotations中所做的那样
public class CustomAttribute : Attribute
{
public CustomAttribute(Type resourceType, string resourceName)
{
Message = ResourceHelper.GetResourceLookup(resourceType, resourceName);
}
public string Message { get; set; }
}
public class ResourceHelper
{
public static string GetResourceLookup(Type resourceType, string resourceName)
{
if ((resourceType != null) && (resourceName != null))
{
PropertyInfo property = resourceType.GetProperty(resourceName, BindingFlags.Public | BindingFlags.Static);
if (property == null)
{
throw new InvalidOperationException(string.Format("Resource Type Does Not Have Property"));
}
if (property.PropertyType != typeof(string))
{
throw new InvalidOperationException(string.Format("Resource Property is Not String Type"));
}
return (string)property.GetValue(null, null);
}
return null;
}
}
以下是我编写的修订版:
[System.AttributeUsage(System.AttributeTargets.Class, AllowMultiple = false)]
public class ProviderIconAttribute : Attribute
{
public Image ProviderIcon { get; protected set; }
public ProviderIconAttribute(Type resourceType, string resourceName)
{
var value = ResourceHelper.GetResourceLookup<Image>(resourceType, resourceName);
this.ProviderIcon = value;
}
}
//From http://stackoverflow.com/questions/1150874/c-sharp-attribute-text-from-resource-file
//Only thing I changed was adding NonPublic to binding flags since our images come from other dll's
// and making it generic, as the original only supports strings
public class ResourceHelper
{
public static T GetResourceLookup<T>(Type resourceType, string resourceName)
{
if ((resourceType != null) && (resourceName != null))
{
PropertyInfo property = resourceType.GetProperty(resourceName, BindingFlags.Public | BindingFlags.Static | BindingFlags.NonPublic);
if (property == null)
{
return default(T);
}
return (T)property.GetValue(null, null);
}
return default(T);
}
}
[System.AttributeUsage(System.AttributeTargets.Class,AllowMultiple=false)]
公共类ProviderConAttribute:属性
{
公共映像提供程序图标{get;protected set;}
public ProviderIconAttribute(类型resourceType,字符串resourceName)
{
var value=ResourceHelper.GetResourceLookup(resourceType,resourceName);
this.ProviderIcon=值;
}
}
//从http://stackoverflow.com/questions/1150874/c-sharp-attribute-text-from-resource-file
//我唯一改变的是添加非公开的绑定标志,因为我们的图片来自其他dll
//并使其通用,因为原始版本仅支持字符串
公共类ResourceHelper
{
公共静态T GetResourceLookup(类型resourceType,字符串resourceName)
{
if((resourceType!=null)&&(resourceName!=null))
{
PropertyInfo property=resourceType.GetProperty(resourceName,BindingFlags.Public | BindingFlags.Static | BindingFlags.NonPublic);
if(属性==null)
{
返回默认值(T);
}
return(T)property.GetValue(null,null);
}
返回默认值(T);
}
}
以下是我写的一些东西,因为我找不到任何其他东西可以做到这一点:
输入
在项目a中编写一个常量字符串类
[GenerateResource]
public static class ResourceFileName
{
public static class ThisSupports
{
public static class NestedClasses
{
[Comment("Comment value")]
public const string ResourceKey = "Resource Value";
}
}
}
输出
并且将在包含constants类的项目中生成一个资源
您所需要做的就是将此代码放在某个地方:
来源
public class CommentAttribute : Attribute
{
public CommentAttribute(string comment)
{
this.Comment = comment;
}
public string Comment { get; set; }
}
public class GenerateResourceAttribute : Attribute
{
public string FileName { get; set; }
}
public class ResourceGenerator
{
public ResourceGenerator(IEnumerable<Assembly> assemblies)
{
// Loop over the provided assemblies.
foreach (var assembly in assemblies)
{
// Loop over each type in the assembly.
foreach (var type in assembly.GetTypes())
{
// See if the type has the GenerateResource attribute.
var attribute = type.GetCustomAttribute<GenerateResourceAttribute>(false);
if (attribute != null)
{
// If so determine the output directory. First assume it's the current directory.
var outputDirectory = Directory.GetCurrentDirectory();
// Is this assembly part of the output directory?
var index = outputDirectory.LastIndexOf(typeof(ResourceGenerator).Assembly.GetName().Name);
if (index >= 0)
{
// If so remove it and anything after it.
outputDirectory = outputDirectory.Substring(0, index);
// Is the concatenation of the output directory and the target assembly name not a directory?
outputDirectory = Path.Combine(outputDirectory, type.Assembly.GetName().Name);
if (!Directory.Exists(outputDirectory))
{
// If that is the case make it the current directory.
outputDirectory = Directory.GetCurrentDirectory();
}
}
// Use the default file name (Type + "Resources") if one was not provided.
var fileName = attribute.FileName;
if (fileName == null)
{
fileName = type.Name + "Resources";
}
// Add .resx to the end of the file name.
fileName = Path.Combine(outputDirectory, fileName);
if (!fileName.EndsWith(".resx", StringComparison.InvariantCultureIgnoreCase))
{
fileName += ".resx";
}
using (var resx = new ResXResourceWriter(fileName))
{
var tuples = this.GetTuplesRecursive("", type).OrderBy(t => t.Item1);
foreach (var tuple in tuples)
{
var key = tuple.Item1 + tuple.Item2.Name;
var value = tuple.Item2.GetValue(null);
string comment = null;
var commentAttribute = tuple.Item2.GetCustomAttribute<CommentAttribute>();
if (commentAttribute != null)
{
comment = commentAttribute.Comment;
}
resx.AddResource(new ResXDataNode(key, value) { Comment = comment });
}
}
}
}
}
}
private IEnumerable<Tuple<string, FieldInfo>> GetTuplesRecursive(string prefix, Type type)
{
// Get the properties for the current type.
foreach (var field in type.GetFields(BindingFlags.Public | BindingFlags.Static))
{
yield return new Tuple<string, FieldInfo>(prefix, field);
}
// Get the properties for each child type.
foreach (var nestedType in type.GetNestedTypes())
{
foreach (var tuple in this.GetTuplesRecursive(prefix + nestedType.Name, nestedType))
{
yield return tuple;
}
}
}
}
然后,您的属性可以使用静态类ResourceFileName.ThisSupports.NestedClasses.ResourceKey
,而其他代码可以使用资源文件
[IntegerValidation(1, 70, ErrorMessage = Data.Messages.Speed)]
private int i_Speed;
您可能需要根据您的特定需要对其进行定制。我有一个类似的案例,需要将资源字符串放入属性中。在C#6中,我们有
nameof()
功能,而这似乎就是关键所在
在我的例子中,我可以使用[SomeAttribute(nameof(Resources.SomeResourceKey))]
并且它编译得很好。然后我只需要在另一端做一些工作,使用该值从资源文件中获取正确的字符串
在您的情况下,您可以尝试:
[IntegerValidation(1, 70, ErrorMessageResourceKey = nameof(Data.Messages.Speed))]
private int i_Speed;
然后,您可以按照(伪代码)执行以下操作:
我在属性的显示名称方面遇到了这个问题,我做了以下更改: 对于我们的资源文件,我将自定义工具属性更改为
PublicResXFileCodeGenerator
然后将其添加到属性中:
[Display(Name = "MyResourceName", ResourceType = typeof(Resources.MyResources))]
如果您使用的是.NET 3.5或更新版本,则可以使用
ErrorMessageResourceName
和ErrorMessageResourceType
参数
比如说
[必需(ErrorMessageResourceName=“attribute_name”,ErrorMessageResourceType=typeof(resource_file_type))]
我将此方法设置为通用方法,并将非公共方法添加到绑定列表中,因此我可以从其他dll中提取资源,效果非常好。我实际上是在用它来获取图像资源。@Peter-你能发布你修改过的解决方案吗?@Andrew-我在一个开源项目中使用它。您需要挖掘一下,但这里是指向属性类的链接:@Andrew-我继续,并将其作为单独的答案发布。[RegularExpression(“.++.+\\\..+”,ErrorMessageResourceName=“InvalidEmail”,ErrorMessageResourceType=typeof(Resources.Registration))]以下是从资源文件中获取数据的步骤1-添加资源文件名,例如Captions.resx 2-添加一些字符串及其值,如FirstName 3-注意:可能会出现错误无法检索属性“FirstName”,因为本地化失败。类型“Xxxx.Captions”不是公共的或不包含名为“FirstName”的公共静态字符串属性。“。这是因为,默认情况下,资源文件属性的访问修饰符设置为内部。请在资源文件工具栏的访问修饰符下拉列表中将其更改为公共。4-这是代码4-这是代码[必需的(ErrorMessageResourceName=“RequiredFirstName”,ErrorMessageResourceType=typeof(Resources.Captions))]//[必需(ErrorMessage=“请输入系统名称”)][显示(Name=“FirstName”,ResourceType=typeof(Resources.Captions))]公共字符串系统名称{get;set;}
[Display(Name = "MyResourceName", ResourceType = typeof(Resources.MyResources))]