Вопрос по c#, attributes, resources – Текст атрибута C # из файла ресурсов?

28

У меня есть атрибут, и я хочу загрузить текст в атрибут из файла ресурсов.

[IntegerValidation(1, 70, ErrorMessage = Data.Messages.Speed)]
private int i_Speed;

Но я продолжаю получать & quot; аргумент атрибута должен быть выражением константы, выражением typeof или выражением создания массива типа параметра атрибута & quot;

Это прекрасно работает, если я добавлю строку вместо Data.Messages.Text, например:

[IntegerValidation(1, 70, ErrorMessage = "Invalid max speed")]

Есть идеи?

4 - Вот код [Обязательный (ErrorMessageResourceName = & quot; RequiredFirstName & quot ;, ErrorMessageResourceType = typeof (Resources.Captions))] // [Обязательный (ErrorMessage = & Пожалуйста, введите имя системы & quot;) & quot; FirstName & quot; FirstName & quot; FirstName & quot; FirstName & quot; FirstName & quot; FirstName & quot; ;, ResourceType = typeof (Resources.Captions))] публичная строка SystemName {get; задавать; } Asad Naeem
Ниже приведены шаги для извлечения данных из файлов ресурсов. 1. Добавьте файл ресурсов, например, Captions.resx 2. Добавьте строку со своими значениями, например FirstName 3 - & lt; b & gt; Примечание: & lt; / b & gt; Может появиться ошибка & # x201C; Невозможно получить свойство 'FirstName & apos; потому что локализация не удалась. Введите «Xxxx.Captions». не является общедоступным или не содержит открытого статического строкового свойства с именем «FirstName». & # x201C ;. Это связано с тем, что по умолчанию в свойствах файла ресурсов для модификатора доступа установлено значение internal. Измените его на «Открытый» в раскрывающемся списке «Модификатор доступа» на панели инструментов файла ресурсов. 4 - вот код Asad Naeem

Ваш Ответ

8   ответов
0

Вот что я написал, так как не смог найти ничего другого, что делает это.

Input

Написать константный класс строки в проекте А.

[GenerateResource]
public static class ResourceFileName
{
    public static class ThisSupports
    {
        public static class NestedClasses
        {
            [Comment("Comment value")]
            public const string ResourceKey = "Resource Value";
        }
    }
}

Output

И в проекте будет создан ресурс, содержащий класс констант.

enter image description here

Все, что вам нужно сделать, это где-нибудь иметь этот код:

Source

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;
            }
        }
    }
}

А затем сделайте небольшой проект, который имеет ссылку на все ваши сборки с[GenerateResource]

public class Program
{
    static void Main(string[] args)
    {
        var assemblies = AppDomain.CurrentDomain.GetAssemblies().ToList();
        string path = Directory.GetCurrentDirectory();
        foreach (string dll in Directory.GetFiles(path, "*.dll"))
        {
            assemblies.Add(Assembly.LoadFile(dll));
        }
        assemblies = assemblies.Distinct().ToList();

        new ResourceGenerator(assemblies);
    }
}

Тогда ваши атрибуты могут использовать статический классResourceFileName.ThisSupports.NestedClasses.ResourceKey в то время как другой код может использовать файл ресурсов.

Возможно, вам придется адаптировать его к вашим конкретным потребностям.

1

Я столкнулся с этой проблемой с отображаемым именем для атрибута, и я сделал следующие изменения:

Для нашего файла ресурсов я изменил свойство пользовательского инструмента наPublicResXFileCodeGenerator

Затем добавил это к атрибуту:

[Display(Name = "MyResourceName", ResourceType = typeof(Resources.MyResources))]
0

У меня есть похожий случай, когда мне нужно поместить строки ресурсов в атрибуты. В C # 6 у нас естьnameof() способность, и это, кажется, делает свое дело.

В моем случае я могу использовать[SomeAttribute(nameof(Resources.SomeResourceKey))] и компилируется нормально. Затем мне просто нужно немного поработать на другом конце, чтобы использовать это значение для получения правильной строки из файла ресурсов.

В вашем случае вы можете попробовать:

[IntegerValidation(1, 70, ErrorMessageResourceKey = nameof(Data.Messages.Speed))]
private int i_Speed;

Затем вы можете сделать что-то вроде (псевдокод):

Properties.Resources.ResourceManager.GetString(attribute.ErrorMessageResourceKey);
1

Природа атрибутов такова, что данные, которые вы помещаете в свойства атрибутов, должны быть константами. Эти значения будут храниться в сборке, но никогда не приведут к выполнению скомпилированного кода. Таким образом, вы не можете иметь значения атрибутов, которые должны выполняться для вычисления результатов.

28

Вот мое решение. Я добавил свойства 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; 
    }
}
@Peter - не могли бы вы опубликовать ваше модифицированное решение?
Я сделал этот метод универсальным и добавил NonPublic в список Binding, чтобы я мог извлекать ресурсы из других библиотек DLL, и он прекрасно работает. Я на самом деле использую его, чтобы вытащить ресурсы изображения.
@ Andrew - я использую это в проекте с открытым исходным кодом. Вам придется немного покопаться, но здесь есть ссылка на класс атрибута:pulse.codeplex.com/SourceControl/changeset/view/71556#1475612
@ Андрей - я пошел дальше и разместил это как отдельный ответ также.
24

Значения атрибута жестко запрограммированы в сборке при компиляции. Если вы хотите что-то сделать во время выполнения, вам нужно будет использовать константу в качествеkey, затем поместите некоторый код в сам класс атрибута, чтобы загрузить ресурс.

9

Вот модифицированная версия той, которую я собрал:

[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);
        }
    }
1

Используйте строку, которая является именем ресурса. .NET делает это с некоторыми внутренними атрибутами.

[RegularExpression (& quot;. + @. + \\ .. + ", ErrorMessageResourceName = & quot; InvalidEmail & quot ;, ErrorMessageResourceType = typeof (Resources.Registration))]

Похожие вопросы