Skip to content

Commit d01dfc9

Browse files
authored
Merge pull request #9676 from h3xds1nz/mouseconverter-speedup
Optimize conversion of MouseAction enum from/to string, remove allocations
2 parents e86ce74 + ee01b27 commit d01dfc9

File tree

1 file changed

+62
-87
lines changed

1 file changed

+62
-87
lines changed

src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/Command/MouseActionConverter.cs

Lines changed: 62 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -9,140 +9,115 @@
99
// to the *Type* that the string represents
1010
//
1111

12-
using System;
1312
using System.ComponentModel; // for TypeConverter
1413
using System.Globalization; // for CultureInfo
15-
using System.Reflection;
16-
using MS.Internal;
17-
using System.Windows;
18-
using System.Windows.Input;
19-
using MS.Utility;
2014

21-
using SR=MS.Internal.PresentationCore.SR;
15+
using SR = MS.Internal.PresentationCore.SR;
2216

2317
namespace System.Windows.Input
2418
{
2519
/// <summary>
26-
/// MouseAction - Converter class for converting between a string and the Type of a MouseAction
20+
/// Converter class for converting between a <see langword="string"/> and <see cref="MouseAction"/>.
2721
/// </summary>
2822
public class MouseActionConverter : TypeConverter
2923
{
3024
///<summary>
31-
/// CanConvertFrom - Used to check whether we can convert a string into a MouseAction
25+
/// Used to check whether we can convert a <see langword="string"/> into a <see cref="MouseAction"/>.
3226
///</summary>
3327
///<param name="context">ITypeDescriptorContext</param>
3428
///<param name="sourceType">type to convert from</param>
35-
///<returns>true if the given type can be converted, false otherwise</returns>
29+
///<returns><see langword="true"/> if the given <paramref name="sourceType"/> can be converted from, <see langword="false"/> otherwise.</returns>
3630
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
3731
{
38-
// We can only handle string.
39-
if (sourceType == typeof(string))
40-
{
41-
return true;
42-
}
43-
else
44-
{
45-
return false;
46-
}
32+
// We can only handle string
33+
return sourceType == typeof(string);
4734
}
4835

49-
50-
///<summary>
51-
///TypeConverter method override.
52-
///</summary>
53-
///<param name="context">ITypeDescriptorContext</param>
54-
///<param name="destinationType">Type to convert to</param>
55-
///<returns>true if conversion is possible</returns>
36+
/// <summary>
37+
/// Used to check whether we can convert specified value to <see langword="string"/>.
38+
/// </summary>
39+
/// <param name="context">ITypeDescriptorContext</param>
40+
/// <param name="destinationType">Type to convert to</param>
41+
/// <returns><see langword="true"/> if conversion to <see langword="string"/> is possible, <see langword="false"/> otherwise.</returns>
5642
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
5743
{
58-
// We can convert to an InstanceDescriptor or to a string.
59-
if (destinationType == typeof(string))
60-
{
61-
// When invoked by the serialization engine we can convert to string only for known type
62-
if (context != null && context.Instance != null)
63-
{
64-
return (MouseActionConverter.IsDefinedMouseAction((MouseAction)context.Instance));
65-
}
66-
}
67-
return false;
44+
// We can convert to an InstanceDescriptor or to a string
45+
if (destinationType != typeof(string))
46+
return false;
47+
48+
// When invoked by the serialization engine we can convert to string only for known type
49+
if (context is null || context.Instance is null)
50+
return false;
51+
52+
// Make sure the value falls within defined set
53+
return IsDefinedMouseAction((MouseAction)context.Instance);
6854
}
6955

7056
/// <summary>
71-
/// ConvertFrom()
57+
/// Converts <paramref name="source"/> of <see langword="string"/> type to its <see cref="MouseAction"/> represensation.
7258
/// </summary>
7359
/// <param name="context">Parser Context</param>
7460
/// <param name="culture">Culture Info</param>
7561
/// <param name="source">MouseAction String</param>
76-
/// <returns></returns>
62+
/// <returns>A <see cref="MouseAction"/> representing the <see langword="string"/> specified by <paramref name="source"/>.</returns>
7763
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object source)
7864
{
79-
if (source != null && source is string)
65+
if (source is not string mouseAction)
66+
throw GetConvertFromException(source);
67+
68+
ReadOnlySpan<char> mouseActionToken = mouseAction.AsSpan().Trim();
69+
return mouseActionToken switch
8070
{
81-
string mouseActionToken = ((string)source).Trim();
82-
mouseActionToken = mouseActionToken.ToUpper(CultureInfo.InvariantCulture);
83-
if (mouseActionToken == String.Empty)
84-
return MouseAction.None;
85-
86-
MouseAction mouseAction = MouseAction.None;
87-
switch (mouseActionToken)
88-
{
89-
case "NONE" : mouseAction = MouseAction.None; break;
90-
case "LEFTCLICK" : mouseAction = MouseAction.LeftClick; break;
91-
case "RIGHTCLICK" : mouseAction = MouseAction.RightClick; break;
92-
case "MIDDLECLICK" : mouseAction = MouseAction.MiddleClick; break;
93-
case "WHEELCLICK" : mouseAction = MouseAction.WheelClick; break;
94-
case "LEFTDOUBLECLICK" : mouseAction = MouseAction.LeftDoubleClick; break;
95-
case "RIGHTDOUBLECLICK" : mouseAction = MouseAction.RightDoubleClick; break;
96-
case "MIDDLEDOUBLECLICK": mouseAction = MouseAction.MiddleDoubleClick; break;
97-
default :
98-
throw new NotSupportedException(SR.Format(SR.Unsupported_MouseAction, mouseActionToken));
99-
}
100-
return mouseAction;
101-
}
102-
throw GetConvertFromException(source);
71+
_ when mouseActionToken.IsEmpty => MouseAction.None, // Special casing as produced by "ConvertTo"
72+
_ when mouseActionToken.Equals("None", StringComparison.OrdinalIgnoreCase) => MouseAction.None,
73+
_ when mouseActionToken.Equals("LeftClick", StringComparison.OrdinalIgnoreCase) => MouseAction.LeftClick,
74+
_ when mouseActionToken.Equals("RightClick", StringComparison.OrdinalIgnoreCase) => MouseAction.RightClick,
75+
_ when mouseActionToken.Equals("MiddleClick", StringComparison.OrdinalIgnoreCase) => MouseAction.MiddleClick,
76+
_ when mouseActionToken.Equals("WheelClick", StringComparison.OrdinalIgnoreCase) => MouseAction.WheelClick,
77+
_ when mouseActionToken.Equals("LeftDoubleClick", StringComparison.OrdinalIgnoreCase) => MouseAction.LeftDoubleClick,
78+
_ when mouseActionToken.Equals("RightDoubleClick", StringComparison.OrdinalIgnoreCase) => MouseAction.RightDoubleClick,
79+
_ when mouseActionToken.Equals("MiddleDoubleClick", StringComparison.OrdinalIgnoreCase) => MouseAction.MiddleDoubleClick,
80+
_ => throw new NotSupportedException(SR.Format(SR.Unsupported_MouseAction, mouseActionToken.ToString()))
81+
};
10382
}
10483

10584
/// <summary>
106-
/// ConvertTo()
85+
/// Converts a <paramref name="value"/> of <see cref="MouseAction"/> to its <see langword="string"/> represensation.
10786
/// </summary>
10887
/// <param name="context">Serialization Context</param>
10988
/// <param name="culture">Culture Info</param>
11089
/// <param name="value">MouseAction value </param>
11190
/// <param name="destinationType">Type to Convert</param>
112-
/// <returns>string if parameter is a MouseAction</returns>
91+
/// <returns>A <see langword="string"/> representing the <see cref="MouseAction"/> specified by <paramref name="value"/>.</returns>
11392
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
11493
{
11594
ArgumentNullException.ThrowIfNull(destinationType);
11695

117-
if (destinationType == typeof(string) && value != null)
96+
if (value is null || destinationType != typeof(string))
97+
throw GetConvertToException(value, destinationType);
98+
99+
return (MouseAction)value switch
118100
{
119-
MouseAction mouseActionValue = (MouseAction)value ;
120-
if (MouseActionConverter.IsDefinedMouseAction(mouseActionValue))
121-
{
122-
string mouseAction = null;
123-
switch (mouseActionValue)
124-
{
125-
case MouseAction.None : mouseAction=String.Empty; break;
126-
case MouseAction.LeftClick : mouseAction="LeftClick"; break;
127-
case MouseAction.RightClick : mouseAction="RightClick"; break;
128-
case MouseAction.MiddleClick : mouseAction="MiddleClick"; break;
129-
case MouseAction.WheelClick : mouseAction="WheelClick"; break;
130-
case MouseAction.LeftDoubleClick : mouseAction="LeftDoubleClick"; break;
131-
case MouseAction.RightDoubleClick : mouseAction="RightDoubleClick"; break;
132-
case MouseAction.MiddleDoubleClick: mouseAction="MiddleDoubleClick"; break;
133-
}
134-
if (mouseAction != null)
135-
return mouseAction;
136-
}
137-
throw new InvalidEnumArgumentException("value", (int)mouseActionValue, typeof(MouseAction));
138-
}
139-
throw GetConvertToException(value,destinationType);
101+
MouseAction.None => string.Empty,
102+
MouseAction.LeftClick => "LeftClick",
103+
MouseAction.RightClick => "RightClick",
104+
MouseAction.MiddleClick => "MiddleClick",
105+
MouseAction.WheelClick => "WheelClick",
106+
MouseAction.LeftDoubleClick => "LeftDoubleClick",
107+
MouseAction.RightDoubleClick => "RightDoubleClick",
108+
MouseAction.MiddleDoubleClick => "MiddleDoubleClick",
109+
_ => throw new InvalidEnumArgumentException(nameof(value), (int)value, typeof(MouseAction))
110+
};
140111
}
141112

142-
// Helper like Enum.IsDefined, for MouseAction.
113+
/// <summary>
114+
/// Helper function similar to <see cref="Enum.IsDefined{MouseAction}(MouseAction)"/>, just lighter and faster.
115+
/// </summary>
116+
/// <param name="mouseAction">The value to test against.</param>
117+
/// <returns><see langword="true"/> if <paramref name="mouseAction"/> falls in enumeration range, <see langword="false"/> otherwise.</returns>
143118
internal static bool IsDefinedMouseAction(MouseAction mouseAction)
144119
{
145-
return (mouseAction >= MouseAction.None && mouseAction <= MouseAction.MiddleDoubleClick);
120+
return mouseAction >= MouseAction.None && mouseAction <= MouseAction.MiddleDoubleClick;
146121
}
147122
}
148123
}

0 commit comments

Comments
 (0)