Skip to content

Commit

Permalink
Fix ToEnumString performance
Browse files Browse the repository at this point in the history
  • Loading branch information
exyi authored and acizmarik committed Jul 27, 2022
1 parent 8208a84 commit d354646
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 25 deletions.
29 changes: 16 additions & 13 deletions src/Framework/Framework/Controls/HtmlGenericControl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -370,19 +370,22 @@ private void AddHtmlAttribute(IHtmlWriter writer, string name, object? value)
{
writer.AddAttribute(name, ReflectionUtils.ToEnumString(enumValue.GetType(), enumValue.ToString()));
}
else if (value is Guid)
{
writer.AddAttribute(name, value.ToString());
}
else if (ReflectionUtils.IsNumericType(value.GetType()))
{
writer.AddAttribute(name, Convert.ToString(value, CultureInfo.InvariantCulture));
}
else
{
// DateTime and related are not supported here intentionally.
// It is not clear in which format it should be rendered - on some places, the HTML specs requires just yyyy-MM-dd,
// but in case of Web Components, the users may want to pass the whole date, or use a specific format
}

private static string AttributeValueToString(object? value) =>
value switch {
null => "",
string str => str,
Enum enumValue => ReflectionUtils.ToEnumString(enumValue.GetType(), enumValue.ToString()),
Guid guid => guid.ToString(),
_ when ReflectionUtils.IsNumericType(value.GetType()) => Convert.ToString(value, CultureInfo.InvariantCulture) ?? "",
System.Collections.IEnumerable =>
throw new NotSupportedException($"Attribute value of type '{value.GetType().ToCode(stripNamespace: true)}' is not supported. Consider concatenating the values into a string or use the HtmlGenericControl.AttributeList if you need to pass multiple values."),
_ =>

// DateTime and related are not supported here intentionally.
// It is not clear in which format it should be rendered - on some places, the HTML specs requires just yyyy-MM-dd,
// but in case of Web Components, the users may want to pass the whole date, or use a specific format

throw new NotSupportedException($"Attribute value of type '{value.GetType().FullName}' is not supported. Please convert the value to string, e. g. by using ToString()");
}
Expand Down
33 changes: 21 additions & 12 deletions src/Framework/Framework/Utils/ReflectionUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -472,20 +472,12 @@ public static string GetTypeHash(this Type type)
member is TypeInfo type ? type.AsType() :
throw new NotImplementedException($"Could not get return type of member {member.GetType().FullName}");

private static readonly ConcurrentDictionary<Type, bool> hasEnumMemberFieldCache = new();

public static string ToEnumString<T>(this T instance) where T : Enum
public static string ToEnumString<T>(this T instance) where T : struct, Enum
{
var enumType = instance.GetType();

var hasEnumMemberField = hasEnumMemberFieldCache.GetOrAdd(enumType,
t => t.GetFields(BindingFlags.Public | BindingFlags.Static)
.Any(field => field.IsDefined(typeof(EnumMemberAttribute), false)));

var name = instance.ToString();
if (!hasEnumMemberField)
return name;
return ToEnumString(enumType, name);
if (!EnumInfo<T>.HasEnumMemberField)
return name;
return ToEnumString(typeof(T), name);
}

public static string ToEnumString(Type enumType, string name)
Expand All @@ -501,6 +493,23 @@ public static string ToEnumString(Type enumType, string name)
}
return name;
}

internal static class EnumInfo<T> where T: struct, Enum
{
internal static readonly bool HasEnumMemberField;

static EnumInfo()
{
foreach (var field in typeof(T).GetFields(BindingFlags.Public | BindingFlags.Static))
{
if (field.IsDefined(typeof(EnumMemberAttribute), false))
{
HasEnumMemberField = true;
break;
}
}
}
}

public static Type GetDelegateType(MethodInfo methodInfo)
{
Expand Down

0 comments on commit d354646

Please sign in to comment.