Reflexia - konverzia anemickych objektov do IEnumerable<Tuple<string, object>> rubrika: Programování: .Net
Ako skonvertovat vsetky public properties do seq<string * obj>
(v C# je to IEnumerable<Tuple<string, object>>
) kde string bude nazov property a obj bude boxovana hodnota?
Napisal som si na to funkciu ktora najprv pomocou reflexie precita vsetky public properties a vrati pole PropertyInfo a potom im rad radom cita hodnoty a vracia ich v IEnumerable v spravnom type.
Zo zaciatku pri par entitach nebol problem. Ale teraz pri 1970 entitach (kazda ma 24 properties) trva cietanie 4 sekundy. Co je na obycajne hlupe citanie dat velmi dlho. Zistil ze uzke hrdlo je PropertyInfo.GetValue ktora je pomala, pri beznom citani sa to neprejavi ale pri tisickach iteracii ano. Ako to teda cele zrytchlit pripadne poradte nejaky NuGet balicek na tento ucel.
let toSeq (o : 't) = o.GetType().GetProperties(BindingFlags.Public ||| BindingFlags.Instance) |> Seq.choose (fun p -> if p.CanRead && p.GetIndexParameters().Length = 0 then if o.GetType() = p.PropertyType then None else let value = try p.GetValue(o) with :? Exception as e -> null Some(p.Name, value) else None)
Tu je priklad mam to napisane v F# ale aj pre C#ckarov je asi jasne co sa v priklade deje keby nie takj mozem to prepisat do C#.
harrison314 vdaka inspiroval som sa tvojim prispevkom, nieco som k tomu pogooglil, ale s expression trees nemam ziadne skusenosti a nie celkom tomu rozumiem. A vo vysledku je to rovnako pomale ako predchadzajuca varianta. Takze bud mam nieco zle, pripadne mam niekde logicku chybu, alebo som nieco opomenul. Asi to necham na inokedy musi sa mi to ulezat v hlave.
// edit1: upravena verzia pouzivajuca expression trees, funguje ale neni to o nic rychlejsie.
// edit2: takze nakoniec tato verzia je vporiadku, a je dost rychla, chyba bola v niecom inom. ono v tom boli 2 problemy tento a a este jeden a tym ze som ich obidva fixol som sa dostal z 3.8 sekundy na 0.2 sekundy - pri rovnakom pocte objektov - neuveritelne.
let getterCache = new ConcurrentDictionary<Type, Func<obj, Dictionary<string, obj>>>() let private toDictFactory (objType : Type) = let dict = Expression.Variable(typeof<Dictionary<string, obj>>); let inputExpression = Expression.Parameter(typeof<obj>, "input") let typedInputExpression = Expression.Convert(inputExpression, objType) let dictType = typeof<Dictionary<string, obj>> let add = dictType.GetMethod("Add", BindingFlags.Public ||| BindingFlags.Instance, null, [| typeof<string>; typeof<obj> |], null) let body = new List<Expression>() body.Add(Expression.Assign(dict, Expression.New(typeof<Dictionary<string, obj>>))) let props = objType.GetTypeInfo().GetProperties(BindingFlags.Public ||| BindingFlags.Instance) for p in props do if p.CanRead && p.GetIndexParameters().Length = 0 then let key = Expression.Constant(p.Name) let value = Expression.Property(typedInputExpression, p) let valueAsObject = Expression.Convert(value, typeof<obj>) body.Add(Expression.Call(dict, add, key, valueAsObject)) body.Add(dict) let block = Expression.Block([| dict |], body); let lambda = Expression.Lambda<Func<obj, Dictionary<string, obj>>>(block, inputExpression); lambda.Compile() let toDict (o : obj) = match o with | null -> null | value -> let t = value.GetType() let getter = getterCache.GetOrAdd(t, new Func<Type, Func<obj, Dictionary<string, obj>>>(fun _ -> toDictFactory t)) o |> getter.Invoke let toSeq (o : obj) = match o |> toDict with | null -> null | value -> value |> Seq.map(fun x -> x.Key, x.Value)
Pro zobrazení všech 2 odpovědí se prosím přihlaste:
Nebo se přihlaste jménem a heslem:
Komentáře