c# - 如何使用属性来影响 C# 中的函数?
问题描述
根据C# attribute 中的文档,似乎在 C# 中,Attribute 只能用作编译时元数据存储。您必须使用反射来操作它。
...
public class AnimalTypeAttribute : Attribute {
// The constructor is called when the attribute is set.
public AnimalTypeAttribute(Animal pet) {
thePet = pet;
}
// Keep a variable internally ...
protected Animal thePet;
// .. and show a copy to the outside world.
public Animal Pet {
get { return thePet; }
set { thePet = value; }
}
}
...
class DemoClass {
static void Main(string[] args) {
AnimalTypeTestClass testClass = new AnimalTypeTestClass();
Type type = testClass.GetType();
// Iterate through all the methods of the class.
foreach(MethodInfo mInfo in type.GetMethods()) {
// Iterate through all the Attributes for each method.
foreach (Attribute attr in
Attribute.GetCustomAttributes(mInfo)) {
// Check for the AnimalType attribute.
if (attr.GetType() == typeof(AnimalTypeAttribute))
Console.WriteLine(
"Method {0} has a pet {1} attribute.",
mInfo.Name, ((AnimalTypeAttribute)attr).Pet);
}
}
}
}
但是,我注意到FlagsAttribute没有添加任何变量。虽然,它已经操纵/拦截了 ToString() 的输出(我猜)。FlagsAttribute 如何做到这一点?如何模仿行为,或影响自定义属性中的某些功能?
[FlagsAttribute]
enum MultiHue : short {
None = 0,
Black = 1,
Red = 2,
Green = 4,
Blue = 8
};
...
Console.WriteLine( "{0,3} - {1:G}", 3, (MultiHue)3); // output 3 - Black, Red
解决方案
阅读代码Enum.cs
,我们可以看到该ToString
方法调用InternalFormat
:
public override string ToString()
{
// Returns the value in a human readable format. For PASCAL style enums who's value maps directly the name of the field is returned.
// For PASCAL style enums who's values do not map directly the decimal value of the field is returned.
// For BitFlags (indicated by the Flags custom attribute): If for each bit that is set in the value there is a corresponding constant
// (a pure power of 2), then the OR string (ie "Red, Yellow") is returned. Otherwise, if the value is zero or if you can't create a string that consists of
// pure powers of 2 OR-ed together, you return a hex value
// Try to see if its one of the enum values, then we return a String back else the value
return InternalFormat((RuntimeType)GetType(), ToUInt64()) ?? ValueToString();
}
private static string InternalFormat(RuntimeType eT, ulong value)
{
Debug.Assert(eT != null);
// These values are sorted by value. Don't change this
TypeValuesAndNames entry = GetCachedValuesAndNames(eT, true);
if (!entry.IsFlag) // Not marked with Flags attribute
{
return Enum.GetEnumName(eT, value);
}
else // These are flags OR'ed together (We treat everything as unsigned types)
{
return InternalFlagsFormat(eT, entry, value);
}
}
并InternalFormat
调用GetCachedValuesAndNames
访问信息缓存。
在这个GetCachedValuesAndNames
方法中,我们可以看到它检查是否FlagsAttribute
已定义 ( bool isFlags = enumType.IsDefined(typeof(FlagsAttribute), inherit: false);
):
private static TypeValuesAndNames GetCachedValuesAndNames(RuntimeType enumType, bool getNames)
{
TypeValuesAndNames entry = enumType.GenericCache as TypeValuesAndNames;
if (entry == null || (getNames && entry.Names == null))
{
ulong[] values = null;
string[] names = null;
GetEnumValuesAndNames(
enumType.GetTypeHandleInternal(),
JitHelpers.GetObjectHandleOnStack(ref values),
JitHelpers.GetObjectHandleOnStack(ref names),
getNames);
bool isFlags = enumType.IsDefined(typeof(FlagsAttribute), inherit: false);
entry = new TypeValuesAndNames(isFlags, values, names);
enumType.GenericCache = entry;
}
return entry;
}
所以它确实使用反射来确定是否FlagsAttribute
存在,并相应地调整ToString
结果。
推荐阅读
- python - 如何使用for循环将来自各种csv的列添加到新的csv
- rust - 添加 Rust 和 Webassembly
- spring-data-elasticsearch - spring-data-elasticsearch 对 elasticsearch 版本 7.14 的支持
- excel - Excel DisplayAlerts 在使用 GetObject 打开工作簿时失败
- php - 电报 Laravel 机器人回复
- ios - 使用 ARGeoAnchor 显示 3D 模型
- microsoft-edge - 信息亭模式下的 Edge 失去焦点
- flutter - Dart - 如何检查字符串是否包含搜索输入中的每个单词?
- html - 如何复制网页的文本内容
- flutter - Transform.scale 减小小部件的大小,即:CostumSwitch 但原始空间保持不变?