diff --git a/src/Umbraco.Core/ObjectExtensions.cs b/src/Umbraco.Core/ObjectExtensions.cs index 928de44d72..e4c9d94eba 100644 --- a/src/Umbraco.Core/ObjectExtensions.cs +++ b/src/Umbraco.Core/ObjectExtensions.cs @@ -12,11 +12,11 @@ using Umbraco.Core.Collections; namespace Umbraco.Core { - /// - /// Provides object extension methods. - /// - public static class ObjectExtensions - { + /// + /// Provides object extension methods. + /// + public static class ObjectExtensions + { // Cache the various type lookups private static readonly ConcurrentDictionary NullableGenericCache = new ConcurrentDictionary(); private static readonly ConcurrentDictionary InputTypeConverterCache = new ConcurrentDictionary(); @@ -36,33 +36,33 @@ namespace Umbraco.Core /// /// public static IEnumerable AsEnumerableOfOne(this T input) - { - return Enumerable.Repeat(input, 1); - } + { + return Enumerable.Repeat(input, 1); + } - /// - /// - /// - /// - public static void DisposeIfDisposable(this object input) - { - var disposable = input as IDisposable; - if (disposable != null) disposable.Dispose(); - } + /// + /// + /// + /// + public static void DisposeIfDisposable(this object input) + { + var disposable = input as IDisposable; + if (disposable != null) disposable.Dispose(); + } - /// - /// Provides a shortcut way of safely casting an input when you cannot guarantee the is - /// an instance type (i.e., when the C# AS keyword is not applicable). - /// - /// - /// The input. - /// - internal static T SafeCast(this object input) - { - if (ReferenceEquals(null, input) || ReferenceEquals(default(T), input)) return default(T); - if (input is T) return (T)input; - return default(T); - } + /// + /// Provides a shortcut way of safely casting an input when you cannot guarantee the is + /// an instance type (i.e., when the C# AS keyword is not applicable). + /// + /// + /// The input. + /// + internal static T SafeCast(this object input) + { + if (ReferenceEquals(null, input) || ReferenceEquals(default(T), input)) return default(T); + if (input is T) return (T)input; + return default(T); + } /// /// Attempts to convert the input object to the output type. @@ -76,12 +76,12 @@ namespace Umbraco.Core var result = TryConvertTo(input, typeof(T)); if (result.Success) - return Attempt.Succeed((T) result.Result); + return Attempt.Succeed((T)result.Result); // just try to cast try { - return Attempt.Succeed((T) input); + return Attempt.Succeed((T)input); } catch (Exception e) { @@ -180,7 +180,7 @@ namespace Umbraco.Core // TODO: Do a check for destination type being IEnumerable and source type implementing IEnumerable with // the same 'T', then we'd have to find the extension method for the type AsEnumerable() and execute it. - if (GetCachedCanAssign(inputType, target)) + if (GetCachedCanAssign(input, inputType, target)) { return Attempt.Succeed(Convert.ChangeType(input, target)); } @@ -379,92 +379,92 @@ namespace Umbraco.Core // E_NOTIMPL IPAddress, BigInteger return null; // We can't decide... } - internal static void CheckThrowObjectDisposed(this IDisposable disposable, bool isDisposed, string objectname) - { - //TODO: Localise this exception - if (isDisposed) - throw new ObjectDisposedException(objectname); - } + internal static void CheckThrowObjectDisposed(this IDisposable disposable, bool isDisposed, string objectname) + { + //TODO: Localise this exception + if (isDisposed) + throw new ObjectDisposedException(objectname); + } - //public enum PropertyNamesCaseType - //{ - // CamelCase, - // CaseInsensitive - //} + //public enum PropertyNamesCaseType + //{ + // CamelCase, + // CaseInsensitive + //} - ///// - ///// Convert an object to a JSON string with camelCase formatting - ///// - ///// - ///// - //public static string ToJsonString(this object obj) - //{ - // return obj.ToJsonString(PropertyNamesCaseType.CamelCase); - //} + ///// + ///// Convert an object to a JSON string with camelCase formatting + ///// + ///// + ///// + //public static string ToJsonString(this object obj) + //{ + // return obj.ToJsonString(PropertyNamesCaseType.CamelCase); + //} - ///// - ///// Convert an object to a JSON string with the specified formatting - ///// - ///// The obj. - ///// Type of the property names case. - ///// - //public static string ToJsonString(this object obj, PropertyNamesCaseType propertyNamesCaseType) - //{ - // var type = obj.GetType(); - // var dateTimeStyle = "yyyy-MM-dd HH:mm:ss"; + ///// + ///// Convert an object to a JSON string with the specified formatting + ///// + ///// The obj. + ///// Type of the property names case. + ///// + //public static string ToJsonString(this object obj, PropertyNamesCaseType propertyNamesCaseType) + //{ + // var type = obj.GetType(); + // var dateTimeStyle = "yyyy-MM-dd HH:mm:ss"; - // if (type.IsPrimitive || typeof(string).IsAssignableFrom(type)) - // { - // return obj.ToString(); - // } + // if (type.IsPrimitive || typeof(string).IsAssignableFrom(type)) + // { + // return obj.ToString(); + // } - // if (typeof(DateTime).IsAssignableFrom(type) || typeof(DateTimeOffset).IsAssignableFrom(type)) - // { - // return Convert.ToDateTime(obj).ToString(dateTimeStyle); - // } + // if (typeof(DateTime).IsAssignableFrom(type) || typeof(DateTimeOffset).IsAssignableFrom(type)) + // { + // return Convert.ToDateTime(obj).ToString(dateTimeStyle); + // } - // var serializer = new JsonSerializer(); + // var serializer = new JsonSerializer(); - // switch (propertyNamesCaseType) - // { - // case PropertyNamesCaseType.CamelCase: - // serializer.ContractResolver = new CamelCasePropertyNamesContractResolver(); - // break; - // } + // switch (propertyNamesCaseType) + // { + // case PropertyNamesCaseType.CamelCase: + // serializer.ContractResolver = new CamelCasePropertyNamesContractResolver(); + // break; + // } - // var dateTimeConverter = new IsoDateTimeConverter - // { - // DateTimeStyles = System.Globalization.DateTimeStyles.None, - // DateTimeFormat = dateTimeStyle - // }; + // var dateTimeConverter = new IsoDateTimeConverter + // { + // DateTimeStyles = System.Globalization.DateTimeStyles.None, + // DateTimeFormat = dateTimeStyle + // }; - // if (typeof(IDictionary).IsAssignableFrom(type)) - // { - // return JObject.FromObject(obj, serializer).ToString(Formatting.None, dateTimeConverter); - // } + // if (typeof(IDictionary).IsAssignableFrom(type)) + // { + // return JObject.FromObject(obj, serializer).ToString(Formatting.None, dateTimeConverter); + // } - // if (type.IsArray || (typeof(IEnumerable).IsAssignableFrom(type))) - // { - // return JArray.FromObject(obj, serializer).ToString(Formatting.None, dateTimeConverter); - // } + // if (type.IsArray || (typeof(IEnumerable).IsAssignableFrom(type))) + // { + // return JArray.FromObject(obj, serializer).ToString(Formatting.None, dateTimeConverter); + // } - // return JObject.FromObject(obj, serializer).ToString(Formatting.None, dateTimeConverter); - //} + // return JObject.FromObject(obj, serializer).ToString(Formatting.None, dateTimeConverter); + //} - /// - /// Converts an object into a dictionary - /// - /// - /// - /// - /// - /// - /// + /// + /// Converts an object into a dictionary + /// + /// + /// + /// + /// + /// + /// public static IDictionary ToDictionary(this T o, - params Expression>[] ignoreProperties) - { - return o.ToDictionary(ignoreProperties.Select(e => o.GetPropertyInfo(e)).Select(propInfo => propInfo.Name).ToArray()); - } + params Expression>[] ignoreProperties) + { + return o.ToDictionary(ignoreProperties.Select(e => o.GetPropertyInfo(e)).Select(propInfo => propInfo.Name).ToArray()); + } /// /// Turns object into dictionary @@ -473,137 +473,137 @@ namespace Umbraco.Core /// Properties to ignore /// public static IDictionary ToDictionary(this object o, params string[] ignoreProperties) - { - if (o != null) - { - var props = TypeDescriptor.GetProperties(o); - var d = new Dictionary(); - foreach (var prop in props.Cast().Where(x => ignoreProperties.Contains(x.Name) == false)) - { - var val = prop.GetValue(o); - if (val != null) - { - d.Add(prop.Name, (TVal)val); - } - } - return d; - } - return new Dictionary(); - } + { + if (o != null) + { + var props = TypeDescriptor.GetProperties(o); + var d = new Dictionary(); + foreach (var prop in props.Cast().Where(x => ignoreProperties.Contains(x.Name) == false)) + { + var val = prop.GetValue(o); + if (val != null) + { + d.Add(prop.Name, (TVal)val); + } + } + return d; + } + return new Dictionary(); + } - internal static string ToDebugString(this object obj, int levels = 0) - { - if (obj == null) return "{null}"; - try - { - if (obj is string) - { - return "\"{0}\"".InvariantFormat(obj); - } + internal static string ToDebugString(this object obj, int levels = 0) + { + if (obj == null) return "{null}"; + try + { + if (obj is string) + { + return "\"{0}\"".InvariantFormat(obj); + } if (obj is int || obj is Int16 || obj is Int64 || obj is float || obj is double || obj is bool || obj is int? || obj is Int16? || obj is Int64? || obj is float? || obj is double? || obj is bool?) - { - return "{0}".InvariantFormat(obj); - } - if (obj is Enum) - { - return "[{0}]".InvariantFormat(obj); - } - if (obj is IEnumerable) - { - var enumerable = (obj as IEnumerable); + { + return "{0}".InvariantFormat(obj); + } + if (obj is Enum) + { + return "[{0}]".InvariantFormat(obj); + } + if (obj is IEnumerable) + { + var enumerable = (obj as IEnumerable); - var items = (from object enumItem in enumerable let value = GetEnumPropertyDebugString(enumItem, levels) where value != null select value).Take(10).ToList(); + var items = (from object enumItem in enumerable let value = GetEnumPropertyDebugString(enumItem, levels) where value != null select value).Take(10).ToList(); - return items.Any() - ? "{{ {0} }}".InvariantFormat(String.Join(", ", items)) - : null; - } + return items.Any() + ? "{{ {0} }}".InvariantFormat(String.Join(", ", items)) + : null; + } - var props = obj.GetType().GetProperties(); - if ((props.Length == 2) && props[0].Name == "Key" && props[1].Name == "Value" && levels > -2) - { - try - { - var key = props[0].GetValue(obj, null) as string; - var value = props[1].GetValue(obj, null).ToDebugString(levels - 1); - return "{0}={1}".InvariantFormat(key, value); - } - catch (Exception) - { - return "[KeyValuePropertyException]"; - } - } - if (levels > -1) - { - var items = - (from propertyInfo in props - let value = GetPropertyDebugString(propertyInfo, obj, levels) - where value != null - select "{0}={1}".InvariantFormat(propertyInfo.Name, value)).ToArray(); + var props = obj.GetType().GetProperties(); + if ((props.Length == 2) && props[0].Name == "Key" && props[1].Name == "Value" && levels > -2) + { + try + { + var key = props[0].GetValue(obj, null) as string; + var value = props[1].GetValue(obj, null).ToDebugString(levels - 1); + return "{0}={1}".InvariantFormat(key, value); + } + catch (Exception) + { + return "[KeyValuePropertyException]"; + } + } + if (levels > -1) + { + var items = + (from propertyInfo in props + let value = GetPropertyDebugString(propertyInfo, obj, levels) + where value != null + select "{0}={1}".InvariantFormat(propertyInfo.Name, value)).ToArray(); - return items.Any() - ? "[{0}]:{{ {1} }}".InvariantFormat(obj.GetType().Name, String.Join(", ", items)) - : null; - } - } - catch (Exception ex) - { - return "[Exception:{0}]".InvariantFormat(ex.Message); - } - return null; - } + return items.Any() + ? "[{0}]:{{ {1} }}".InvariantFormat(obj.GetType().Name, String.Join(", ", items)) + : null; + } + } + catch (Exception ex) + { + return "[Exception:{0}]".InvariantFormat(ex.Message); + } + return null; + } - /// - /// Attempts to serialize the value to an XmlString using ToXmlString - /// - /// - /// - /// - internal static Attempt TryConvertToXmlString(this object value, Type type) - { - try - { - var output = value.ToXmlString(type); - return Attempt.Succeed(output); - } - catch (NotSupportedException ex) - { - return Attempt.Fail(ex); - } - } + /// + /// Attempts to serialize the value to an XmlString using ToXmlString + /// + /// + /// + /// + internal static Attempt TryConvertToXmlString(this object value, Type type) + { + try + { + var output = value.ToXmlString(type); + return Attempt.Succeed(output); + } + catch (NotSupportedException ex) + { + return Attempt.Fail(ex); + } + } - /// - /// Returns an XmlSerialized safe string representation for the value - /// - /// - /// The Type can only be a primitive type or Guid and byte[] otherwise an exception is thrown - /// - internal static string ToXmlString(this object value, Type type) - { - if (value == null) return string.Empty; - if (type == typeof(string)) return (value.ToString().IsNullOrWhiteSpace() ? "" : value.ToString()); - if (type == typeof(bool)) return XmlConvert.ToString((bool)value); - if (type == typeof(byte)) return XmlConvert.ToString((byte)value); - if (type == typeof(char)) return XmlConvert.ToString((char)value); + /// + /// Returns an XmlSerialized safe string representation for the value + /// + /// + /// The Type can only be a primitive type or Guid and byte[] otherwise an exception is thrown + /// + internal static string ToXmlString(this object value, Type type) + { + if (value == null) return string.Empty; + if (type == typeof(string)) return (value.ToString().IsNullOrWhiteSpace() ? "" : value.ToString()); + if (type == typeof(bool)) return XmlConvert.ToString((bool)value); + if (type == typeof(byte)) return XmlConvert.ToString((byte)value); + if (type == typeof(char)) return XmlConvert.ToString((char)value); if (type == typeof(DateTime)) return XmlConvert.ToString((DateTime)value, XmlDateTimeSerializationMode.Unspecified); - if (type == typeof(DateTimeOffset)) return XmlConvert.ToString((DateTimeOffset)value); - if (type == typeof(decimal)) return XmlConvert.ToString((decimal)value); - if (type == typeof(double)) return XmlConvert.ToString((double)value); - if (type == typeof(float)) return XmlConvert.ToString((float)value); - if (type == typeof(Guid)) return XmlConvert.ToString((Guid)value); - if (type == typeof(int)) return XmlConvert.ToString((int)value); - if (type == typeof(long)) return XmlConvert.ToString((long)value); - if (type == typeof(sbyte)) return XmlConvert.ToString((sbyte)value); - if (type == typeof(short)) return XmlConvert.ToString((short)value); - if (type == typeof(TimeSpan)) return XmlConvert.ToString((TimeSpan)value); - if (type == typeof(bool)) return XmlConvert.ToString((bool)value); - if (type == typeof(uint)) return XmlConvert.ToString((uint)value); - if (type == typeof(ulong)) return XmlConvert.ToString((ulong)value); - if (type == typeof(ushort)) return XmlConvert.ToString((ushort)value); + if (type == typeof(DateTimeOffset)) return XmlConvert.ToString((DateTimeOffset)value); + if (type == typeof(decimal)) return XmlConvert.ToString((decimal)value); + if (type == typeof(double)) return XmlConvert.ToString((double)value); + if (type == typeof(float)) return XmlConvert.ToString((float)value); + if (type == typeof(Guid)) return XmlConvert.ToString((Guid)value); + if (type == typeof(int)) return XmlConvert.ToString((int)value); + if (type == typeof(long)) return XmlConvert.ToString((long)value); + if (type == typeof(sbyte)) return XmlConvert.ToString((sbyte)value); + if (type == typeof(short)) return XmlConvert.ToString((short)value); + if (type == typeof(TimeSpan)) return XmlConvert.ToString((TimeSpan)value); + if (type == typeof(bool)) return XmlConvert.ToString((bool)value); + if (type == typeof(uint)) return XmlConvert.ToString((uint)value); + if (type == typeof(ulong)) return XmlConvert.ToString((ulong)value); + if (type == typeof(ushort)) return XmlConvert.ToString((ushort)value); - throw new NotSupportedException("Cannot convert type " + type.FullName + " to a string using ToXmlString as it is not supported by XmlConvert"); - } + throw new NotSupportedException("Cannot convert type " + type.FullName + " to a string using ToXmlString as it is not supported by XmlConvert"); + } /// /// Returns an XmlSerialized safe string representation for the value and type @@ -612,38 +612,38 @@ namespace Umbraco.Core /// /// internal static string ToXmlString(this object value) - { - return value.ToXmlString(typeof (T)); - } + { + return value.ToXmlString(typeof(T)); + } - private static string GetEnumPropertyDebugString(object enumItem, int levels) - { - try - { - return enumItem.ToDebugString(levels - 1); - } - catch (Exception) - { - return "[GetEnumPartException]"; - } - } + private static string GetEnumPropertyDebugString(object enumItem, int levels) + { + try + { + return enumItem.ToDebugString(levels - 1); + } + catch (Exception) + { + return "[GetEnumPartException]"; + } + } - private static string GetPropertyDebugString(PropertyInfo propertyInfo, object obj, int levels) - { - try - { - return propertyInfo.GetValue(obj, null).ToDebugString(levels - 1); - } - catch (Exception) - { - return "[GetPropertyValueException]"; - } - } + private static string GetPropertyDebugString(PropertyInfo propertyInfo, object obj, int levels) + { + try + { + return propertyInfo.GetValue(obj, null).ToDebugString(levels - 1); + } + catch (Exception) + { + return "[GetPropertyValueException]"; + } + } - internal static Guid AsGuid(this object value) - { - return value is Guid ? (Guid) value : Guid.Empty; - } + internal static Guid AsGuid(this object value) + { + return value is Guid ? (Guid)value : Guid.Empty; + } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static string NormalizeNumberDecimalSeparator(string s) @@ -656,57 +656,93 @@ namespace Umbraco.Core [MethodImpl(MethodImplOptions.AggressiveInlining)] private static TypeConverter GetCachedSourceTypeConverter(Type source, Type target) { - return InputTypeConverterCache.GetOrAdd(new CompositeTypeTypeKey(source, target), k => - { - var ksource = k.Type1; - var ktarget = k.Type2; + var key = new CompositeTypeTypeKey(source, target); - var converter = TypeDescriptor.GetConverter(ksource); - return converter.CanConvertTo(ktarget) ? converter : null; - }); + if (InputTypeConverterCache.TryGetValue(key, out TypeConverter typeConverter)) + { + return typeConverter; + } + + TypeConverter converter = TypeDescriptor.GetConverter(source); + if (converter.CanConvertTo(target)) + { + return InputTypeConverterCache[key] = converter; + } + + return InputTypeConverterCache[key] = null; } // gets a converter for target, that can convert from source, or null if none exists [MethodImpl(MethodImplOptions.AggressiveInlining)] private static TypeConverter GetCachedTargetTypeConverter(Type source, Type target) { - return DestinationTypeConverterCache.GetOrAdd(new CompositeTypeTypeKey(source, target), k => - { - var ksource = k.Type1; - var ktarget = k.Type2; + var key = new CompositeTypeTypeKey(source, target); - var converter = TypeDescriptor.GetConverter(ktarget); - return converter.CanConvertFrom(ksource) ? converter : null; - }); + if (DestinationTypeConverterCache.TryGetValue(key, out TypeConverter typeConverter)) + { + return typeConverter; + } + + TypeConverter converter = TypeDescriptor.GetConverter(target); + if (converter.CanConvertFrom(source)) + { + return DestinationTypeConverterCache[key] = converter; + } + + return DestinationTypeConverterCache[key] = null; } // gets the underlying type of a nullable type, or null if the type is not nullable [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Type GetCachedGenericNullableType(Type type) { - return NullableGenericCache.GetOrAdd(type, t - => t.GetGenericTypeDefinition() == typeof(Nullable<>) ? Nullable.GetUnderlyingType(type) : null; + if (NullableGenericCache.TryGetValue(type, out Type underlyingType)) + { + return underlyingType; + } + + if (type.GetGenericTypeDefinition() == typeof(Nullable<>)) + { + Type underlying = Nullable.GetUnderlyingType(type); + return NullableGenericCache[type] = underlying; + } + + return NullableGenericCache[type] = null; } // gets an IConvertible from source to target type, or null if none exists [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool GetCachedCanAssign(Type source, Type target) + private static bool GetCachedCanAssign(object input, Type source, Type target) { - return AssignableTypeCache.GetOrAdd(new CompositeTypeTypeKey(source, target), k => + var key = new CompositeTypeTypeKey(source, target); + if (AssignableTypeCache.TryGetValue(key, out bool canConvert)) { - var ksource = k.Type1; - var ktarget = k.Type2; + return canConvert; + } - return ktarget.IsAssignableFrom(ksource) && typeof(IConvertible).IsAssignableFrom(ksource); - }); + if (input is IConvertible && target.IsAssignableFrom(source)) + { + return AssignableTypeCache[key] = true; + } + + return AssignableTypeCache[key] = false; } // determines whether a type can be converted to boolean [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool GetCachedCanConvertToBoolean(Type type) { - return BoolConvertCache.GetOrAdd(type, t - => CustomBooleanTypeConverter.CanConvertFrom(t)); + if (BoolConvertCache.TryGetValue(type, out bool result)) + { + return result; + } + + if (CustomBooleanTypeConverter.CanConvertFrom(type)) + { + return BoolConvertCache[type] = true; + } + + return BoolConvertCache[type] = false; } } } \ No newline at end of file diff --git a/src/Umbraco.Tests.Benchmarks/ConcurrentDictionaryBenchmarks.cs b/src/Umbraco.Tests.Benchmarks/ConcurrentDictionaryBenchmarks.cs new file mode 100644 index 0000000000..6cb39b7235 --- /dev/null +++ b/src/Umbraco.Tests.Benchmarks/ConcurrentDictionaryBenchmarks.cs @@ -0,0 +1,152 @@ +using BenchmarkDotNet.Attributes; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Umbraco.Core.Collections; + +namespace Umbraco.Tests.Benchmarks +{ + [MemoryDiagnoser] + public class ConcurrentDictionaryBenchmarks + { + private static readonly ConcurrentDictionary AssignableTypeCache = new ConcurrentDictionary(); + + private static object input = new Bar(); + + private static Type source = typeof(Bar); + + private static Type target = typeof(Foo); + + [Benchmark(Baseline = true)] + public bool GetCachedCanAssignFactory() + { + return AssignableTypeCache.GetOrAdd(new CompositeTypeTypeKey(source, target), k => + { + var ksource = k.Type1; + var ktarget = k.Type2; + + return ktarget.IsAssignableFrom(ksource) && typeof(IConvertible).IsAssignableFrom(ksource); + }); + } + + [Benchmark] + public bool GetCachedCanAssignNoFactory() + { + // This method is 10% faster + var key = new CompositeTypeTypeKey(source, target); + if (AssignableTypeCache.TryGetValue(key, out bool canConvert)) + { + return canConvert; + } + + // "is" is faster than "IsAssignableFrom" + if (input is IConvertible && target.IsAssignableFrom(source)) + { + return AssignableTypeCache[key] = true; + } + + return AssignableTypeCache[key] = false; + } + + private class Foo : IConvertible + { + public TypeCode GetTypeCode() + { + return TypeCode.Object; + } + + public bool ToBoolean(IFormatProvider provider) + { + throw new NotImplementedException(); + } + + public byte ToByte(IFormatProvider provider) + { + throw new NotImplementedException(); + } + + public char ToChar(IFormatProvider provider) + { + throw new NotImplementedException(); + } + + public DateTime ToDateTime(IFormatProvider provider) + { + throw new NotImplementedException(); + } + + public decimal ToDecimal(IFormatProvider provider) + { + throw new NotImplementedException(); + } + + public double ToDouble(IFormatProvider provider) + { + throw new NotImplementedException(); + } + + public short ToInt16(IFormatProvider provider) + { + throw new NotImplementedException(); + } + + public int ToInt32(IFormatProvider provider) + { + throw new NotImplementedException(); + } + + public long ToInt64(IFormatProvider provider) + { + throw new NotImplementedException(); + } + + public sbyte ToSByte(IFormatProvider provider) + { + throw new NotImplementedException(); + } + + public float ToSingle(IFormatProvider provider) + { + throw new NotImplementedException(); + } + + public string ToString(IFormatProvider provider) + { + throw new NotImplementedException(); + } + + public object ToType(Type conversionType, IFormatProvider provider) + { + if (conversionType == typeof(Foo)) + { + return new Foo(); + } + + throw new NotImplementedException(); + } + + public ushort ToUInt16(IFormatProvider provider) + { + throw new NotImplementedException(); + } + + public uint ToUInt32(IFormatProvider provider) + { + throw new NotImplementedException(); + } + + public ulong ToUInt64(IFormatProvider provider) + { + throw new NotImplementedException(); + } + } + + private class Bar : Foo + { + + } + } +} diff --git a/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj b/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj index 2eb4f5f3d3..67e35a3621 100644 --- a/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj +++ b/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj @@ -162,6 +162,7 @@ + @@ -175,7 +176,9 @@ - + + Designer +