Skip to content

Instantly share code, notes, and snippets.

@0x1000000
Created May 3, 2020 14:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 0x1000000/668bb6286042bceba6ecb0f40c1d91d3 to your computer and use it in GitHub Desktop.
Save 0x1000000/668bb6286042bceba6ecb0f40c1d91d3 to your computer and use it in GitHub Desktop.
For https://habr.com/ru/post/499562 (FastReslectionForHabrahabr)
using BenchmarkDotNet.Running;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using BenchmarkDotNet.Attributes;
namespace FastReslectionForHabrahabr
{
public class Program
{
static void Main(string[] args)
{
BenchmarkRunner.Run(typeof(Benchmarks));
Console.ReadKey();
}
}
[MemoryDiagnoser]
[Orderer(BenchmarkDotNet.Order.SummaryOrderPolicy.FastestToSlowest)]
public class Benchmarks
{
[Params(1)]
public int N = 1;
public static string[] RawData =
{
$"ФИО:Иванов Иван Иванович1{Environment.NewLine}Тел.:+78886543422{Environment.NewLine}Возраст:22",
$"Фамилия Имя Отчество:Иванов Иван Иванович2{Environment.NewLine}Тел.:+78886543422{Environment.NewLine}Возраст:22",
$"ФИО:Иванов Иван Иванович3{Environment.NewLine}Телефон:+78886543422{Environment.NewLine}Возраст:22"
};
private PropertyToValueCorrelation[][] _data;
private FastContactHydrator _fastContactHydrator;
private SlowContactHydrator _slowContactHydrator;
[GlobalSetup]
public void SetUp()
{
this._fastContactHydrator = new FastContactHydrator();
this._slowContactHydrator = new SlowContactHydrator();
var parser = new DefaultRawStringParser();
this._data = RawData
.Select(r => ContactHydratorBase.GetPropertiesValuesWithoutLinq(parser, r))
.ToArray();
}
[Benchmark]
public void FastHydration()
{
for (int i = 0; i < N; i++)
{
foreach (var data in this._data)
{
HydrateWithoutLinq(this._fastContactHydrator, data);
}
}
}
[Benchmark]
public void SlowHydration()
{
for (int i = 0; i < N; i++)
{
foreach (var data in this._data)
{
HydrateWithoutLinq(this._slowContactHydrator, data);
}
}
}
private static void HydrateWithoutLinq(ContactHydratorBase parser, PropertyToValueCorrelation[] correlation)
{
var contact = parser.GetContact(correlation);
}
}
//Hydrators
public abstract class ContactHydratorBase
{
protected static readonly string _typeName;
static ContactHydratorBase()
{
var type = typeof(Contact);
_typeName = type.FullName;
}
public static PropertyToValueCorrelation[] GetPropertiesValuesWithoutLinq(DefaultRawStringParser normalizer, string rawData)
{
var result = new List<PropertyToValueCorrelation>(10);
var mailPairs = normalizer.ParseWithoutLinq(rawData: rawData, pairDelimiter: Environment.NewLine);
var mapSchemas = MockHelper.GetFakeData;
foreach (var item in mapSchemas)
{
if (!item.EntityName.Equals(_typeName, StringComparison.InvariantCultureIgnoreCase))
continue;
foreach (var pair in mailPairs)
{
if (!pair.Key.Equals(item.Key, StringComparison.InvariantCultureIgnoreCase))
continue;
result.Add(new PropertyToValueCorrelation { PropertyName = item.Property, Value = pair.Value });
}
}
return result.ToArray();
}
public abstract Contact GetContact(PropertyToValueCorrelation[] correlation);
}
public class FastContactHydrator : ContactHydratorBase
{
private static readonly List<KeyValuePair<string, Action<Contact, string>>> _proprtySettersMap =
new List<KeyValuePair<string, Action<Contact, string>>>();
static FastContactHydrator()
{
var type = typeof(Contact);
foreach (var property in type.GetProperties())
{
_proprtySettersMap.Add(new KeyValuePair<string, Action<Contact, string>>(property.Name, GetSetterAction(property)));
}
}
private static Action<Contact, string> GetSetterAction(PropertyInfo property)
{
var setterInfo = property.GetSetMethod();
var paramValueOriginal = Expression.Parameter(property.PropertyType, "value");
var paramEntity = Expression.Parameter(typeof(Contact), "entity");
var setterExp = Expression.Call(paramEntity, setterInfo, paramValueOriginal).Reduce();
var lambda = (Expression<Action<Contact, string>>)Expression.Lambda(setterExp, paramEntity, paramValueOriginal);
return lambda.Compile();
}
public override Contact GetContact(PropertyToValueCorrelation[] correlations)
{
var contact = new Contact();
foreach (var setterMapItem in _proprtySettersMap)
{
var correlation = correlations.FirstOrDefault(x => x.PropertyName == setterMapItem.Key);
setterMapItem.Value(contact, correlation?.Value);
}
return contact;
}
}
public class SlowContactHydrator : ContactHydratorBase
{
protected static readonly PropertyInfo[] _properties;
static SlowContactHydrator()
{
_properties = typeof(Contact).GetProperties();
}
public override Contact GetContact(PropertyToValueCorrelation[] correlations)
{
var contact = new Contact();
foreach (var property in _properties)
{
var correlation = correlations.FirstOrDefault(x => x.PropertyName == property.Name);
if (correlation?.Value == null)
continue;
property.SetValue(contact, correlation.Value);
}
return contact;
}
}
//Model
public class Contact
{
public string FullName { get; set; }
public string Phone { get; set; }
public string Age { get; set; }
}
public class ContactMapSchema
{
public string EntityName { get; set; }
public string Key { get; set; }
public string Property { get; set; }
}
public class PropertyToValueCorrelation
{
public string PropertyName { get; set; }
public string Value { get; set; }
}
//Parser
public class DefaultRawStringParser
{
private static readonly string _unrecognizedKey = "Unrecognized";
public Dictionary<string, string> ParseWithoutLinq(string rawData, string keyValueDelimiter = ":", string pairDelimiter = ";")
{
var result = new Dictionary<string, string>();
var splitted = rawData?.Split(pairDelimiter);
foreach (var item in splitted)
{
var pair = item.Split(keyValueDelimiter, StringSplitOptions.RemoveEmptyEntries);
if (pair.Length == 2)
result.Add(pair[0].Trim(), pair[1].Trim());
else
result.Add(_unrecognizedKey, pair[0].Trim());
}
return result;
}
}
public static class MockHelper
{
public static List<ContactMapSchema> GetFakeData
=> new List<ContactMapSchema>
{
new ContactMapSchema { EntityName = _contactAssemblyName, Key = "Телефон", Property = _contactPhoneAssemblyName},
new ContactMapSchema { EntityName = _contactAssemblyName, Key = "Контактный телефон", Property = _contactPhoneAssemblyName},
new ContactMapSchema { EntityName = _contactAssemblyName, Key = "Тел.", Property = _contactPhoneAssemblyName},
new ContactMapSchema { EntityName = _contactAssemblyName, Key = "ФИО", Property = _contactFullNameAssemblyName},
new ContactMapSchema { EntityName = _contactAssemblyName, Key = "Фамилия Имя Отчество", Property = _contactFullNameAssemblyName},
new ContactMapSchema { EntityName = _contactAssemblyName, Key = "Имя", Property = _contactFullNameAssemblyName},
new ContactMapSchema { EntityName = _contactAssemblyName, Key = "Возраст", Property = _contactAgeAssemblyName},
new ContactMapSchema { EntityName = _contactAssemblyName, Key = "Полных лет", Property = _contactAgeAssemblyName},
};
private static string _contactAssemblyName = typeof(Contact).FullName;
private static string _contactPhoneAssemblyName = nameof(Contact.Phone);
private static string _contactFullNameAssemblyName = nameof(Contact.FullName);
private static string _contactAgeAssemblyName = nameof(Contact.Age);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment