Archive for the ‘C#’ Category

Szűrés opcionális paraméterekre LINQ-val

Tuesday, December 8th, 2009

A feladvány, hogy van sok szűrési feltétel, ezekre szűrni kell, ha van érvényes értékük, vagy kihagyni a szűrésből, ha nincs.
A feladatra SQL Server esetén bool algebrás megoldást írnék:


select * from tabla
where
(@p1 is null) or (@p1 = col1)
and
(@p2 is null) or (@p2 = col2)
...
option(recompile)

Az option hint azért kell, mert így a nullos paraméterek teljesen kiesnek a tervből, csak a valódi szűrésekre készül terv. Ez szerintem óriási dolog, érdemes észben tartani.

Ugyanezt a logikát linqval is el lehet játszani, ref típusokkal kb. így:


var x = from z in Valami
where (p1 == null) || (p1 = z.col1)
&&
(p2 == null) || (p2 = z.col2)

Egy másik megoldásban azt használjuk ki, hogy késleltetett módon értékelődnek ki a kifejezések, így lehet őket láncolni. Itt látható ez a megoldás, és még más megközelítések is.

A harmadik megoldásban írhatunk egy saját szűrő operátort is erre a célra. Az előbbi címről:


public static IQueryable<TSource> WhereIf<TSource>(
    this IQueryable<TSource> source, bool condition,
    Expression<Func<TSource, bool>> predicate)
{
    if (condition)
        return source.Where(predicate);
    else
        return source;
}

Nehéz megindokolni, melyik megoldás a jobb.

Patterns for Parallel Programming: Understanding and Applying Parallel Patterns with the .NET Framework 4

Tuesday, December 1st, 2009

120 oldalas kis olvasmány, alig várom már, hogy legyen egy kis időm elolvasni.

Az tetszik a 4.0-ban, hogy végre megint hozzányúltak az alapokhoz, mint pl. a threadinghez.

Managed memory leak nyomozás WinDBG-vel

Monday, November 30th, 2009

Imádom a WinDbg-t, mondtam már? Az egyik kis programom módszeresen eszegette a memóriát. Ciklusban végez feldolgozást, rettentő sok adattal, és ezek egy része szépen bennragadt a memóriában. .NET-ben nincs memory leak a klasszikus értelemben, de van oly módon, hogy a rootokból marad referencia egyes objektumokra, így azok élve maradnak, szándékaink ellenére. Néha azonban nem triviális kinyomozni, melyik root miatt ragadt be valami a memóriába.
A VSTS profiler segítségével odáig el lehet jutni, hogy ki ragad be a memóriába. Az Object LifeTime nézetben az Instances alive at end oszlopra rendeztetve láthatjuk, kik maradnak élve a program végén. Én beraktam egy force-olt GC-zést a program végére, így aki ezek után még benn maradt, azok egy része szándékaim ellenére tette ezt, ezért azt leaknek tekintem, és meg kell szüntetni.
Jöhet a WinDbg. File, Open Executable, F5. A program végén a GC után raktam még egy Console.ReadLine()-t. Amikor ide eljut, a debuggerben CTRL-Breakkel megállítom a program futását (várakozását). Betöltöm az sos-t:
.load C:\Windows\Microsoft.NET\Framework64\v4.0.21006\sos.dll

Kilistáztatom a GC heapen levő ojjektumokat:
!DumpHeap -stat


000007ff005c2278     2766       221280 System.Data.Metadata.Edm.TypeUsage
000007fef1b90d00      370       222136 System.Byte[]
000007fef1b89be0    13379      1330896 System.String
000007fef1b8c8a8     1125      1447552 System.Int32[]
000007fef1b3bef0     9298      3224392 System.Object[]
000007ff00274988   245954     29514480 ATS.Bar

Akinek nem szabadna már a memóriában lenni, az az ATS.Bar objektumok, 245954 darab, 29514480 bájt méretben. Nézzük megy a példányokat belőle:

!DumpHeap -type ATS.Bar


0000000004952c60 000007ff00274988      120
0000000004952cd8 000007ff00274988      120
0000000004952d50 000007ff00274988      120
0000000004952dc8 000007ff00274988      120
0000000004952e40 000007ff00274988      120
0000000004952eb8 000007ff00274988      120
total 0 objects
Statistics:
              MT    Count    TotalSize Class Name
000007ff00fb38a0        1           24 System.Collections.Generic.GenericEqualityComparer`1[[ATS.BarCollectionDescriptor, Common]]
000007ff00271050        1           24 ATS.BarDAL
000007ff00270e68        1           48 ATS.BarFactory
000007ff00fb31b8        1           88 System.Collections.Generic.Dictionary`2[[System.Int32, mscorlib],[System.Collections.Generic.List`1[[ATS.BarFromTickFactory, Common]], mscorlib]]
000007ff00fb27a8        1           88 System.Collections.Generic.Dictionary`2[[ATS.BarCollectionDescriptor, Common],[ATS.BarFromTickFactory, Common]]
000007ff00275378        1           88 System.Collections.Generic.Dictionary`2[[ATS.Symbol, Common],[ATS.BarCollectionByInterval, Common]]
000007ff00fb3ee0        1           96 System.Collections.Generic.Dictionary`2+Entry[[System.Int32, mscorlib],[System.Collections.Generic.List`1[[ATS.BarFromTickFactory, Common]], mscorlib]][]
000007ff00fb3b10        1           96 System.Collections.Generic.Dictionary`2+Entry[[ATS.BarCollectionDescriptor, Common],[ATS.BarFromTickFactory, Common]][]
000007ff00fb0498        1           96 System.Collections.Generic.Dictionary`2+Entry[[ATS.Symbol, Common],[ATS.BarCollectionByInterval, Common]][]
000007ff00fb2d30        3          120 System.Collections.Generic.List`1[[ATS.BarFromTickFactory, Common]]
000007ff00790990        3          120 ATS.BarCollectionDescriptor
000007ff00fb1568        7          168 System.Collections.ObjectModel.ObservableCollection`1+SimpleMonitor[[ATS.Bar, Common]]
000007ff00886530        3          240 ATS.BarFromTickFactory
000007ff00fb1648        7          280 System.Collections.Generic.List`1[[ATS.Bar, Common]]
000007ff00273788        7          952 ATS.BarCollection
000007ff00274988   245954     29514480 ATS.Bar
Total 245993 objects

A kis 120 bájtos izék a problémásak (a végén az összefoglaló azért tartalmaz több típust is, mert substring szűrést csinál a -type). Az első oszlop az egyedi objektumok címe, a második a típus metódusleíró táblája.
És most jön a lényeg. Ki miatt érhető el a rootokból mondjuk az utolsó Bar példány?

!GCRoot 0000000004952eb8


DOMAIN(00000000003CF530):HANDLE(Pinned):1217d8:Root:  0000000012657048(System.Object[])->
  0000000002bf7f28(System.Collections.Generic.Dictionary`2[[System.Int32, mscorlib],[System.Collections.Generic.List`1[[ATS.BarFromTickFactory, Common]], mscorlib]])->
  0000000002bf94b8(System.Collections.Generic.Dictionary`2+Entry[[System.Int32, mscorlib],[System.Collections.Generic.List`1[[ATS.BarFromTickFactory, Common]], mscorlib]][])->
  0000000002f06ca0(System.Collections.Generic.List`1[[ATS.BarFromTickFactory, Common]])->
  0000000002f06cc8(System.Object[])->
  0000000002f05860(ATS.BarFromTickFactory)->
  0000000004952eb8(ATS.Bar)

Ebben az látszik, az első bejegyzésből, amit még a CLR startup hozzott létre hogyan lehet eljutni a beragadt objektumunkhoz. A CLR generikus neveket C#-ra visszafordítva az látszik, hogy egy Dictionary> hivatkozik egy Dictionary.Entry>-re, az a dictionary belső tárolóeleme. Ez rámutat egy List-re, ami továbbmutat egy ATS.BarFromTickFactory-ra, ami hivatkozik a Bar-ra.

Ez alapján már rekonstruálható a probléma forrása. A probléma elemi oka, hogy túlzásba vittem a statikusok használatát, és nem figyeltem a takarításukra.


static readonly Dictionary<BarCollectionDescriptor, BarFromTickFactory> FactoriesByDesc =
    new Dictionary<BarCollectionDescriptor, BarFromTickFactory>();

Ebből rendesen kiszedtem a tárolt BarFromTickFactory példányt, ha már nem volt rá szükség. De elfeledkeztem róla, hogy volt egy másik kollekció is:


public static readonly Dictionary<int, List<BarFromTickFactory>> FactoryListBySymbolId =
    new Dictionary<int, List<BarFromTickFactory>>();

Ebből viszont nem szedtem ki a hivatkozás az adott BarFromTickFactory-ra, így az szépen beragadt a memóriába.

Tanulságok:
1. Szeretjük a WinDbt-t.
2. Kerüljük a statikusokat. Ha a BarFromTickFactory példányokat egy mások osztály példányai tárolnák, akkor ha azok kifutnak a szkópból, automatikusan a GC martalékai lesznek. A sok statikus sok odafigyelést igényel, kár erőltetni őket.

Apró objektumok szemetelése

Saturday, November 28th, 2009

Az előző bejegyzéshez kapcsolódik még az alábbi. Sok felesleges memóriaallokállást és aztán GC-zést okoztam az alábbi kóddal:


...
OnBarArrived(new BarArrivedEventArgs(bar));
...

        private void OnBarArrived(BarArrivedEventArgs e)
        {
            if (BarArrived != null)
            {
                BarArrived(this, e);
            }
        }

Ha a BarArrived event null, azaz nem iratkozott fel senki az eventre, akkor feleslegesen hozok létre egy BarArrivedEventArgs-ot. A javított verzió így néz ki:


private void OnBarArrived(Bar bar)
{
    if (BarArrived != null)
    {
        BarArrived(this, new BarArrivedEventArgs(bar));
    }
}

A bar objektum már úgyis kész van, az eventargot viszont csak akkor hozom létre, hogy tényleg szükség van rá.

Naív ojjektumhasználat - sok szemét

Friday, November 27th, 2009

Két hete elkezdtem intenzíven használni a Visual Studio 2010-et, mivel szükségem volt a MemoryMappedFiles-ra a 4.0-s fwből. Ezt shared memoryként használom, amivel a laptopon kb. 1GByte/sec-kel tudok adatokat másolni két processz között, így kiváló cache-t tudtam építeni a segítségével. De most nem erről lesz szó.
A profilert is jelentősen továbbfejlesztették, van benne pl. rendes memória allokálás követés (lehet, hogy a régi is tudta ezt, akkor pardon).
Nézzük az alábbi képet:

A SecondInterval.Day property-t közel 6 milliószor hívtam meg, minden híváskor előállítva egy új ojjektumot, 140MByte-nyi szemetet hagyva magam után.

A naív implementáció így nézett ki:


public static SecondInterval Day
{
  get { return new SecondInterval(86400); }
}

Maga a típus egy egyszerű Whole Value pattern implementáció, egy sima immutable objektum, ráadásul class, így referenciális típus.

Nem memóriapazarló módon így néz ki a property:


private static readonly SecondInterval aDay = new SecondInterval(86400);
public static SecondInterval Day
{
    get { return aDay ; }
}

A readonly fontos, hogy nehogy valaki kiüsse a referenciámat, és berakjon egy sunyi másik időt reprezentáló ojjektumot a napi helyére. Az ojjektum immutable, ez fontos, másként semmit nem érne a readonly, a gyomrát lehetne piszkálgatni.
Amin még el lehetne gondolkodni, hogy struktúrává, value type-pá átalakítani a típust, csak akkor meg minden helyen ahol használom, másolni kellene az értékét. Mivel ez most belül egy 32 bites int, és 64 bit alatt futtatom, ahol a referenciák 64 bitesek, a másolással még mindig jobban járok. Úgyhogy lehet, hogy struct lesz, de előbb megnézem classként hogy muzsuikál.

Pár fejlettebb GC dolog

Wednesday, October 14th, 2009

Ezeket érdemes megnézni, érdekesek.

Ha nem szeretnénk, hogy egy rövid, de teljesítményzabáló kódunkat megszakítsa a GC: Latency Modes és Low-Latency GC in .NET 3.5.

Ha interopolunk, és várunk visszahívást nem managed oldalról, de félünk, hogy a GC felszabadítja az ojjektumunkat visszahívás előtt: GC.KeepAlive.

Ha nagyon sok memóriát zabáló appot írunk, és nem szeretnénk belefutni OutOfMemoryException-be, inkább lassítanánk a feldolgozáson: MemoryFailPoint.

Garbage Collection Notifications in .NET 3.5 SP1.

Jó Garbage Collector cikkek

Wednesday, October 14th, 2009

1, 2, 3, 4. Nem csak a szokásos, közismert dolgok, hanem mélyebbek is.
Pl. tudod-e, mekkora memóriát kér el induláskor a GC az OS-től? Hogy 3-féle GC is van (2-t ismer a legtöbb ember, már, aki egyáltalán hallott róla, hogy többféle GC is létezik)? Vagy miért száll el általában egy x86-os .net app 1.5G körüli memóriafelhasználásnál? Stb.

Random orderby Linq és EF használatával

Monday, October 12th, 2009

Az SQL Serveren szokásos orderby newid() szerveroldali megoldást csak linq to SQL esetén tudjuk kihasználni, szerveroldali függvénnyel. Az EF-ben az ilyesmi nem megy:


... orderby Guid.NewID() ...

Ez az EF verzió még nem tudja lefordítani szerveroldali kifejezéssé az orderby kifejezését.
Viszont a random rendezést át lehet nyomni kliensoldalra, ha a lekérdezést “materializáluk” (AsEnumerable) előbb:


Random rnd = new Random();
(from s in ATSEntities.Instance.Symbol
select s).AsEnumerable().OrderBy(o => rnd.Next()));

Nem guidot használtam, hanem Randomot, az kisebb költségű, és az én célomra nem baj, ha csak pszeudo-random a sorrend.

Code Coverage for Concurrency

Thursday, October 8th, 2009

Mostanában rengeteg cikket olvasok az msdnből, rám fér, mert minden hónapban hozza a postás, de párat már ki se nyitottam. Szívom fel magam az oktatások közötti szünetekben, kicsit képbe kerüljek azokkal a cuccokkal, amik a 4.0-ban jönnek be.
A fenti cikk nagyon tetszett, arról ír, hogyan lehet megközelíteni egy többszálú app tesztelését.

Sandcastle és társai

Wednesday, September 30th, 2009

Az NDoc halott, van helyette SandCastle. Nagyon jó, imádom. Ezzel csinálják a VS doksiját is. Van még kétely valakiben?
Az is jó, hogy nem kell kézzel írnom a konfigját, mert van hozzá Sandcastle Help File Builder, ami hasonló GUI, mint az ndochoz volt.
Aztán, hogy ne kelljen sokat gépelni az xml kommenteket, ott a GhostDoc. Beépül a VS-be, és CTRL-ALT-D-vel létrehoz egy komment vázat az adott kódrészhez. Meglepően ügyesen, ha angol neveket használunk a kódban.
Mindhárom eszköz zseniális és INGYENES. Aki ezek után nem dokumentálja a kódját, annak 1-es. :)

C# XML kommentek

Wednesday, September 30th, 2009

Ha valaki szeretne rászánni 2 órát, hogy alaposan megértse, íme egy jó doksi hozzá.

INVARIANT vs. ORDINAL újra

Wednesday, September 23rd, 2009

A korábban írt cikkhez kapcsolódó anyag, ebből már jobban átjön a különbség:
New Recommendations for Using Strings in Microsoft .NET 2.0.

* DO: Use StringComparison.Ordinal or OrdinalIgnoreCase for comparisons as your safe default for culture-agnostic string matching.
* DO: Use StringComparison.Ordinal and OrdinalIgnoreCase comparisons for increased speed.
* DO: Use StringComparison.CurrentCulture-based string operations when displaying the output to the user.
* DO: Switch current use of string operations based on the invariant culture to use the non-linguistic StringComparison.Ordinal or StringComparison.OrdinalIgnoreCase when the comparison is linguistically irrelevant (symbolic, for example).
* DO: Use ToUpperInvariant rather than ToLowerInvariant when normalizing strings for comparison.
* DON’T: Use overloads for string operations that don’t explicitly or implicitly specify the string comparison mechanism.
* DON’T: Use StringComparison.InvariantCulture-based string operations in most cases; one of the few exceptions would be persisting linguistically meaningful but culturally-agnostic data.

Ebből az jön le, hogy használd vagy az ordinalt, vagy a “kulturált” megoldást, de ha lehet ne az invariantot.

# Ordinal comparisons are string comparisons in which each byte of each string is compared without linguistic interpretation. This is essentially a C runtime strcmp. Thus, “windows” would not match “Windows.” Where the context dictates that strings should be matched exactly, or demands conservative matching policy, this comparison should be used. Additionally, ordinal comparisons are the fastest because they apply no linguistic rules when determining a result.

# Case insensitive ordinal comparisons are the next most conservative, and ignore most casing. Thus, “windows” would match “Windows.” When dealing with ASCII characters, this policy is equivalent to that of StringComparison.Ordinal, but with the usual ASCII casing ignored. Thus, any character in [A, Z] (\u0041-\u005A) matches the corresponding one in [a,z] (\u0061-\007A). Casing outside the ASCII range uses the invariant culture’s tables…

Oridinal, case sensitive bináris összehasonlítás, gyors, ok.
Oridinal, case insensitive ascii karakterek esetén egyenlőnek tekinti a kis-nagybetűket, más karakterek esetén átvált az invariantra. Praktikusan szerintem ez azt jelenti, hogy ordinal case insensitive == invariant case insensitive.
Update: Jogos az előbbi, mert: “Comparisons made using OrdinalIgnoreCase are behaviorally the composition of two calls: calling ToUpperInvariant on both string arguments, and doing an Ordinal comparison.”

Közelebbről:


using System;

class Program
{
    static void Main(string[] args)
    {
        string kisalma = "alma";
        string nagyalma = "Alma";
        Compare(kisalma, nagyalma);
        Console.WriteLine();
        kisalma = "őlma";
        nagyalma = "Őlma";
        Compare(kisalma, nagyalma);
    }

    private static void Compare(string a, string b)
    {
        Test(a, b, StringComparison.CurrentCulture);
        Test(a, b, StringComparison.CurrentCultureIgnoreCase);
        Test(a, b, StringComparison.InvariantCulture);
        Test(a, b, StringComparison.InvariantCultureIgnoreCase);
        Test(a, b, StringComparison.Ordinal);
        Test(a, b, StringComparison.OrdinalIgnoreCase);
    }

    private static void Test(string a, string b, StringComparison c)
    {
        int compRes = string.Compare(a, b, c);

        Console.WriteLine("{0}, {1} {2} {3}", c, a,
            compRes == 0 ? "==" : (compRes < 0 ? "<": ">"),
            b);
    }
}

CurrentCulture, alma < Alma
CurrentCultureIgnoreCase, alma == Alma
InvariantCulture, alma < Alma
InvariantCultureIgnoreCase, alma == Alma
Ordinal, alma > Alma
OrdinalIgnoreCase, alma == Alma

CurrentCulture, őlma < Őlma
CurrentCultureIgnoreCase, őlma == Őlma
InvariantCulture, őlma < Őlma
InvariantCultureIgnoreCase, őlma == Őlma
Ordinal, őlma > Őlma
OrdinalIgnoreCase, őlma == Őlma

Nem teljes a teszt, de látható, hogy az ordinal másként sorrendez, mint az invariant, nem nyelvi szempontok szerint, ezért is nem való GUI-hoz, de tudja, hogy a kis ő-nek a nagy Ő a párja. Az invariant se elég okos persze, mivel általános, nem ismeri egy adott nyelv szabályait, pl.


Thread.CurrentThread.CurrentCulture = new CultureInfo("hu-hu");
a = "cukor";
b = "csoki";
Compare(a, b);

CurrentCulture, cukor < csoki
CurrentCultureIgnoreCase, cukor < csoki
InvariantCulture, cukor > csoki
InvariantCultureIgnoreCase, cukor > csoki
Ordinal, cukor > csoki
OrdinalIgnoreCase, cukor > csoki

A cukor után van a csoki magyarban, mivel van csé betűnk. Az invariant értelemszerűen nem tud erről, ezért a csokit cé, es, okinak olvassa, és így is rendez.

Ezek után egyet kell értsek a cikkel, használjuk kulturált kultúrát nyelvi rendezéshez, fájl elérési utakhoz, ojjektum nevekhez, stb. oridinalt, az invariantot meg felejtsük el.
Legalábbis ma így látom. Comments welcome.

MethodBase.GetCurrentMethod Method

Wednesday, September 23rd, 2009

Ismerős? Pl. logoláshoz jól jöhet.

Resharper

Tuesday, September 22nd, 2009

A következő néhány hónapban egy rakat .NET oktatást tartok egy cégnek Marcell jóvoltából (ezúttal is köszönöm neki az üzletet).
A cégnél szabványosítani fogják a Resharpert, egy VS kiegészítő addint. Többször felraktam már a gépemre, de mindig levettem, mert idegesített, hogy mindent átszab a VS-ben. Én mindent shortcutokkal érek el, azokra jól ráül, aztán feljön a saját felülete. Ezzel az agyamra ment, sokat okoskodott, mindig leszedtem.
Most, hogy tanuljam, mire az oktatás jön, újra felraktam, és használom nap mint nap. Telepítés után azt kértem tőle, nem telepedjen rá minden shortcutra, így már egész jól megvagyunk.
Ami viszont igen kellemes meglepetés volt, hogy már két hibát is kiszúrt. Az egyikben egy paraméter értékét az ugyanolyan nevű mezőbe raktam bele, csak valahogy lemaradt a this. Triviális hiba, de nem tűnt fel a sok paraméter között. A másik kicsit ravaszabb.


Process[] p = Process.GetProcessesByName("...");
if (p == null) ...

Mi a hiba eben a kódban? A resharper jól kiszúrta, hogy az if kifejezése mindig true false. Miért? Így néz ki a GetProcessesByName vége:


Process[] array = new Process[list.Count];
list.CopyTo(array, 0);
return array;

Nulla hosszú tömb jön vissza, ha nincs ilyen nevű processz. Nem rakétatudomány, mégis könnyű elrontani. A Resharper meg észrevette. Megtartjuk, szeretjük.

Az IFormattable, IFormatProvider, ICustomFormatter működése

Monday, September 21st, 2009

Ebből pontosan meg lehet érteni, mi zajlik a háttérben.

Mire való az IEquatable?

Monday, September 21st, 2009

Ha value type-ot írsz, és azon gyors, boxolás nélküli egyenlőségvizsgálatot akarsz csinálni, akkor implementáld ezt az interfészt.
Másképp az object.Equals fut le, ami miatt boxolni kell. Hasonló a helyzet az IComparable-lel is.
Érdemes megnézni debuggerben, mikor-mi fut le, ha implementáljuk az interfészt, és ha kikommentezzük az implementációt:


using System;
using System.Collections.Generic;

internal struct MyStruct : IEquatable<MyStruct>
{
    public MyStruct(int a)
    {
        this.a = a;
    }

    private readonly int a;

    public override bool Equals(object obj)
    {
        return base.Equals(obj);
    }
    public override int GetHashCode()
    {
        return base.GetHashCode();
    }

    #region IEquatable<MyStruct> Members

    public bool Equals(MyStruct other)
    {
        return other.a == a;
    }

    #endregion
}
class Program
{
    static void Main()
    {
        List<MyStruct> l = new List<MyStruct> {new MyStruct(33)};
        l.Contains(new MyStruct(22));
    }
}

A jitter huncutságai

Tuesday, August 4th, 2009

Érdekes és furcsa dolgok történnek néha a .net jitter miatt, ami nem nagyon fordul elő nem managelt környezetben.

Itt ez a kis factoryka részlet:


public IBroker GetBroker()
{
    if (broker == null)
    {
        switch (BrokerMode)
        {
            case BrokerMode.Simulated:
                broker = new BrokerSimulated();
                break;
            case BrokerMode.IB:
                broker = IBBroker.Instance;
                break;
        }
    }
    return broker;
}

Amikor meghívtam ezt a sort:


BrokerFactory.Factory.GetBroker().SetInitialEquity(20000);

akkor furcsamód lefutott az élő broker, az IBBroker konstruktora. Pedig NEM az az ág futott le a switchben. Valószínűleg az történt, hogy a jitter hozzáért az IBBroker osztályhoz, ez pedig törvényszerűen kiváltotta a statikus konstruktor lefutását.

A megoldás pofonegyszerű (ha ismerjük az okot :), ki kell emelni a beteg típusra hivatkozást egy külön metódusba. Mivel a jitter metódusonként dolgozik, így addig tényleg nem nyúl a típusunkhoz, míg tényleg nem használjuk:


public IBroker GetBroker()
{
    if (broker == null)
    {
        switch (BrokerMode)
        {
            case BrokerMode.Simulated:
                broker = new BrokerSimulated();
                break;
            case BrokerMode.IB:
                broker = CreateIBBroker();
                break;
        }
    }
    return broker;
}

private static IBBroker CreateIBBroker()
{
    return IBBroker.Instance;
}

Háttérszálból módosítható WPF adatköthető kollekció

Friday, July 10th, 2009

A WPF mint a legtöbb GUI nem szereti, ha más szálakról piszkálják. Sokszor azonban az adatokat más szálak készítik elő megjelenítésre, amit adatkötéssel szeretnénk átadni a GUI-nak. Ilyenkor azonban mindenféle szaftos hibákat fogunk kapni. Egy megoldás a problémára itt található (alsó 4 link), ahol a szerző értelmesen elmagyarázza a probléma okát és megoldását, majd a végén ad konkrét, azonnal használható kódot is.

.NET teljesítményhangolási tapasztalatok 4.

Thursday, February 5th, 2009

Óvakodj az enumtól, mert lassú lészen az - áll a .NET bibliában.

Ártatlan kis jószágnak néz ki ez az enum, mégis sokszor láttam már, hogy miatta lassul be egy rendszer. Mi a lassú rajta? Minden. A Parsolás pl. De ez még érthető is. De hogy a ToString() is tetű, az már kevésbé. Sajnos azonban reflectiont használ ezekhez a műveletekhez, ami köztudottan lassú.
Szerencsére elég könnyű segíteni a baján. Ha Parsolni kell, azaz egy string alapján kell egy enum értéket megszülni, akkor ezt egyszerűen meg lehet oldani egy Dictionary-vel, ami string kulcsokkal és az konkrét enum értékekkel van feltöltve.
Most a másik oldalt mutatom meg, a turbó ToString()-et.
Egyszerű demó enumunk:


public enum TradeDirection
{
    Short,
    Long
}

Ebből tehát ha van egy példányunk amin ToString()-et hívunk, az lassú lesz. Hozzunk létre egy Dictionary-t, ami segít az enum-string asszociációban.


private static Dictionary<TradeDirection, string> tradeDirectionNames = new Dictionary<TradeDirection, string>();

Ezt kellene felölteni értékekkel. A C# generikus dolgaival szépen meg lehet ezt általánosan is fogalmazni, így mindenféle enumra működni fog:


tradeDirectionNames.FillEnumCache();

public static void FillEnumCache<T>(this Dictionary<T, string> enumNames)
{
    foreach (T enumValue in Enum.GetValues(typeof(T)))
    {
        enumNames.Add(enumValue, enumValue.ToString());
    }
}

Mivel extension methodként írtam meg olyan, mintha a Dictionary tudná ezt a funkciót, a T típusparamétert meg kitalálja a compiler, így nem kell kiírjam a teljes alakot:


tradeDirectionNames<TradeDirection>.FillEnumCache();

Hogy jogos-e erre az extension method az objektumorientált szempontból erősen vitatható, de nekem így kézre esett.

A generizálhatóság viszont szenzációs. Ezért imádom a C#-ot.

Miután a fenti megoldással gyorsítottam a rendszeren kiderült (profilerrel, mi mással), hogy egy összetett típusban ami két enumból állt lassú volt a GetHashCode implementációm, ami így nézett ki:


public override int GetHashCode()
{
    return dir.GetHashCode() + zone.GetHashCode();
}

A dir és a zone egy-egy enum.

A gyorsításhoz így írtam át:


public override int GetHashCode()
{
    return (int)dir << 4 + (int)zone;
}

Ez nem csak gyorsabb, de jobban is szór, mint az előző.

Debugolás a .NET fw. forrása segítségével

Thursday, December 11th, 2008

Kaptam egy igen nehezen megközelíthető problémát, amelyben a fókusz a TAB-ra átlépett egyes controlokat. Nem egy triviális TabStop=false probléma volt.
VS 2005-ös projektekről van szó, átkonvertáltam őket 2008-ra, hogy tudjam a fw. forráskódját is debugolni. Az _NT_SYMBOL_PATH= nekem be van állítva a gépen a publikus os szimbólumokra (elsősorban ahhoz amikor WinDbgozok), emiatt nem tudtam a vsből .net fw.-öt debugolni, mert előbb lehúzza a stripped szimbólumokat, a teljeshez már hozzá se nyúl. Ezért egy bat-ból indítom a vs-t, előtte kiütve az eredeti _NT_SYMBOL_PATH-t.
Így már ment a fw. forrás debug, de mivel a clr az ngenelt optimalizált kódot töltötte be, ezért nagyon sok típus belseje nem látszik normálisan. Erre megoldás itt található. Le lehet tiltani, hogy a CLR az ngenelt kódot töltse be, így már rendesen lehet debugolni.
Lehetne, ha nem lenne elcsúszva némelyik forráskód a pdb-ben található sorszámoktól. Ilyenkor van az, hogy teljesen más sorokon lépkedünk végig, mint amit a source ablakban látunk, pl. kommenteken lépked végig a debugger.
A megoldás erre egyszerűbb volt, mint gondoltam volna: próbaképpen kitöröltem 3 sort pl. a Control.cs elejéből, így visszaállt a szinkron.
Maga az alapprobléma egyébként abból adódott, hogy egy kompozit Third Party Contol explicit letiltotta
a TAB-olást, a ControlStyle-ból kivéve a Selectable flaget.