C#自定义扩展方法,用于读取属性属性

huangapple go评论65阅读模式
英文:

C# custom extension method that reads properties attributes

问题

Here are the translations of the code parts and your questions:

我有一个具有各种不同类型属性的类,有些是int,有些是ulong,有些是string等等,它们都非常基本,只有默认的get和set

我希望这些属性有一个自定义方法,返回一个特定格式的字符串,但该格式由每个属性本身决定(我希望在属性本身上编写格式)。

我打算采用的方法是在每个属性上设置一个自定义属性,带有返回格式,类似于这样:
class Users {

    [retFormat("You traveled {0:#,0}km")]
    public int Traveled { get; set; }

    [retFormat("This name is #")]
    public string Name { get; set; } 

}
然后为该数据类型添加一个扩展方法,该方法将读取属性属性并使用它来格式化返回值,这是否可行?
public static string ToUseFormat(this int self) {
    return string.Format("{0:#,0}", self);
    // 此方法必须读取属性属性retFormat并使用它
}

到目前为止,我的问题如下:

1) 这是最佳方式吗?我不想将格式留给类的使用者,我希望每个属性都能返回实际值和格式化值,可能还有多个格式化值,如“长文本”和“短文本”。

2) 如何在方法扩展中读取每个属性的自定义属性?

3) 有没有办法为多种类型创建方法扩展,或者我必须为所有要使用的数据类型创建方法扩展,如int、ulong、string、short等...

Please note that I've translated the code and questions as requested. If you have any further questions or need clarifications, feel free to ask.

英文:

I have a class with several different properties of all types, some are int, some ulong, some string and so on, they are pretty basic and just have a default get and set.

I want those properties to have a custom method that returns a specific formatted string, but that format is decided by each property itself (I want to write the format for each property, on the property itself)

The approach I was going to use is to set a custom attribute on each property with the return format, something like this

    class Users {

        [retFormat("You traveled {0:#,0}km")]
        public int Traveled { get; set; }

        [retFormat("This name is #")]
        public string Name { get; set; } 

    }

And then add a extension method to that data type that will read the attribute property and use it to format the return, is that possible?

    public static string ToUseFormat(this int self) {
        return string.Format("{0:#,0}", self);
        // this method has to read the property attribute retFormat and use it
    }

So far my questions are:

  1. is this the best way to do it? I do not want to leverage the format to the consumer of the class, I want each property to be able to return both the real value and the formatted value, and possible more than 1 formatted value, like "long text" and "short"

  2. How can I read the custom attribute of each property on the method extension?

  3. Is there any way to create a method extension for several types, or i have to create it for all the data types I am going to use it for? like int, ulong, string, short, etc...

答案1

得分: 1

然后为该数据类型添加一个扩展方法,该方法将读取属性属性并使用它来格式化返回值,这是可能的吗?

不,使用 public static string ToUseFormat(this int self) 签名的方法是不可能的 - 它无法确定传递给它的是哪个确切的属性,来自哪个确切的类实例,它允许传递任何整数,因此 42.ToUseFormat() 也是可以的,即使没有涉及属性/类。

这里至少有两种可能的解决方案:

  1. 使用 表达式树反射。以下是一些起步代码(需要大量改进,包括例如空值检查):
class Users {

    [RetFormat("You traveled {0:#,0}km")]
    public int Traveled { get; set; }

    [RetFormat("This name is {0}")]
    public string Name { get; set; } 

}

internal class RetFormatAttribute : Attribute
{
    public string Format { get; }

    public RetFormatAttribute(string format)
    {
        Format = format;
    }
}

public static class Exts
{
    public static string GetFormatted<T, TProp>(this T item, Expression<Func<T, TProp>> getter)
    {
        var memberExpression = getter.Body as MemberExpression;
        var retFormatAttribute = memberExpression.Member.GetCustomAttribute<RetFormatAttribute>();
        if (retFormatAttribute is not null)
        {
            return string.Format(retFormatAttribute.Format, getter.Compile()(item));
        }

        return getter.Compile()(item).ToString();
    }
}

用法示例:

var users = new Users
{
    Name = "Foo",
    Traveled = 42
};

Console.WriteLine(users.GetFormatted(static u => u.Name)); // 输出 "This name is Foo"
Console.WriteLine(users.GetFormatted(static u => u.Traveled)); // 输出 "You traveled 42km"

请注意,这种方法会引入一些性能损耗(尽管对于许多应用程序来说,这可能完全可以接受)。

  1. 使用 源生成器 在编译时生成格式化成员,例如 string TraveledFormatted
英文:

> And then add a extension method to that data type that will read the attribute property and use it to format the return, is that possible?

No, it is not possible by using method with public static string ToUseFormat(this int self) signature - it will have no good way to determine which exact property from which exact class instance was passed to it, it allows passing literally any integer, hence 42.ToUseFormat(), i.e. even no properties/classes involved.

There are at least 2 possible solutions here:

  1. Use expression trees and reflection. Something to get you started (will need a lot of improvement, including null-checks for example):
class Users {

    [RetFormat(&quot;You traveled {0:#,0}km&quot;)]
    public int Traveled { get; set; }

    [RetFormat(&quot;This name is {0}&quot;)]
    public string Name { get; set; } 

}

internal class RetFormatAttribute : Attribute
{
    public string Format { get; }

    public RetFormatAttribute(string format)
    {
        Format = format;
    }
}

public static class Exts
{
    public static string GetFormatted&lt;T, TProp&gt;(this T item, Expression&lt;Func&lt;T, TProp&gt;&gt; getter)
    {
        var memberExpression = getter.Body as MemberExpression;
        var retFormatAttribute = memberExpression.Member.GetCustomAttribute&lt;RetFormatAttribute&gt;();
        if (retFormatAttribute is not null)
        {
            return string.Format(retFormatAttribute.Format, getter.Compile()(item));
        }

        return getter.Compile()(item).ToString();
    }
}

And usage:

var users = new Users
{
    Name = &quot;Foo&quot;,
    Traveled = 42
};

Console.WriteLine(users.GetFormatted(static u =&gt; u.Name)); // prints &quot;This name is Foo&quot;
Console.WriteLine(users.GetFormatted(static u =&gt; u.Traveled)); // prints &quot;You traveled 42km&quot;
Note that this approach will introduce some performance hit (though it can be just fine for quite a lot of apps).
  1. Use source generators to generate formatted members like string TraveledFormatted during compilation time.

答案2

得分: 0

你可以尝试使用DisplayFormat注释:
[DisplayFormat(DataFormatString = &quot;{0:dd/MM/yyyy}&quot;)]

英文:

You can try using the DisplayFormat annotation:

[DisplayFormat(DataFormatString = &quot;{0:dd/MM/yyy}&quot;)]

https://learn.microsoft.com/en-us/dotnet/api/system.componentmodel.dataannotations.displayformatattribute?view=net-7.0

huangapple
  • 本文由 发表于 2023年6月12日 10:06:37
  • 转载请务必保留本文链接:https://go.coder-hub.com/76453274.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定