[c# linq] hoe eigen collecties goed laten werken in linq?

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • BasieP
  • Registratie: Oktober 2000
  • Laatst online: 22-07-2024
Beste mensen,

Ik heb een eigen collectie gemaakt voor mijn eigen objecten, code hieronder:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class CustomObjectCollection : IEnumerable, IEnumerable<CustomObject>
{
    private List<CustomObject> _objecten = new List<CustomObject>();

    public IEnumerator GetEnumerator()
    {
        return (_objecten as IEnumerable).GetEnumerator();
    }

    IEnumerator<CustomObject> IEnumerable<CustomObject>.GetEnumerator()
    {
        return (_objecten as IEnumerable<CustomObject>).GetEnumerator();
    }
}

public class CustomObject
{

    public string getString()
    {
        return "ik ben een string";
    }
}


Nu wil ik graag met link wat kunnen doen met mijn collectie.

echter als ik onderstaande code uitvoer:
C#:
1
2
3
CustomObjectCollection _objs = getObjecten(); //<-- neem maar even aan dat deze gewoon een lijst vult.

List<string> woei = from co in _objs select co.getString();


Dan geeft hij een compile fout bij die tweede regel.
Want 'co' is van het type CustomObjectCollection, en niet van het type CustomObject

Nu is het dus zo dat linq niet ziet dat mijn custom collection een collection is, en gaat dus neit door de objecten heen itereren (ondanks de enumerators)

Hoe fix ik dit?

[ Voor 14% gewijzigd door Woy op 21-04-2011 10:37 . Reden: Verkeerde bericht geedit :X ]

This message was sent on 100% recyclable electrons.


Acties:
  • 0 Henk 'm!

  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
Het default return type van je LINQ expression is een IEnumerable<x> of IQueryable<x> en dus geen list. Je kunt wel de ToList extension method gebruiken.

edit:
Ik zie dat je zegt dat er wat anders mis gaat, maar dat lijkt me sterk. Alle LINQ expressions werken gewoon op IEnumerable<x> of IEnumerable dus dat zou gewoon goed moeten gaan. Welke foutmelding krijg je precies?

[ Voor 42% gewijzigd door Woy op 21-04-2011 10:35 ]

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


Acties:
  • 0 Henk 'm!

  • Phyxion
  • Registratie: April 2004
  • Niet online

Phyxion

_/-\o_

Woy schreef op donderdag 21 april 2011 @ 10:32:
Het default return type van je LINQ expression is een IEnumerable<x> of IQueryable<x> en dus geen list. Je kunt wel de ToList extension method gebruiken.
En ook daarom wil je ReSharper of vergelijkbaar gebruiken, die geeft dit allemaal aan :)

'You like a gay cowboy and you look like a gay terrorist.' - James May


Acties:
  • 0 Henk 'm!

  • BasieP
  • Registratie: Oktober 2000
  • Laatst online: 22-07-2024
Woy schreef op donderdag 21 april 2011 @ 10:32:
Het default return type van je LINQ expression is een IEnumerable<x> of IQueryable<x> en dus geen list. Je kunt wel de ToList extension method gebruiken.
? hoe lost zoiets mijn probleem op?

Als ik van die laatste regel vervang door
C#:
1
IEnumerable<string> woei = from co in _objs select co.getString();

of
C#:
1
List<string> woei = from co in _objs.ToList() select co.getString();


blijft het probleem...

This message was sent on 100% recyclable electrons.


Acties:
  • 0 Henk 'm!

  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
C#:
1
List<string> woei = (from co in _objs select co.getString()).ToList();

Zou volgens mij gewoon moeten werken. Maar wat is de exacte foutmelding die je krijgt?

[ Voor 16% gewijzigd door Woy op 21-04-2011 10:38 ]

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


Acties:
  • 0 Henk 'm!

  • BasieP
  • Registratie: Oktober 2000
  • Laatst online: 22-07-2024
de fout die ik krijg heeft niets met de toewijzing naar 'woei' te maken, maar krijg ik in de linq query zelf.

om even in jip en janneke taal te praten. De rode kringel staat onder 'getString()'
en als ik hover geeft hij aan dat hij 'getString()' niet kan vinden.

Als ik over 'co' hover (ervoor) dan geeft hij aan dat deze van type 'CustomObjectCollection' is.
Ik had verwacht dat dit van type 'CustomObject' zou zijn.

This message was sent on 100% recyclable electrons.


Acties:
  • 0 Henk 'm!

  • Phyxion
  • Registratie: April 2004
  • Niet online

Phyxion

_/-\o_

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace Test
{
    class Program
    {
        public class CustomObjectCollection : IEnumerable, IEnumerable<CustomObject>
        {
            private List<CustomObject> _objecten = new List<CustomObject>();

            public IEnumerator GetEnumerator()
            {
                return (_objecten as IEnumerable).GetEnumerator();
            }

            IEnumerator<CustomObject> IEnumerable<CustomObject>.GetEnumerator()
            {
                return (_objecten as IEnumerable<CustomObject>).GetEnumerator();
            }

            public void Add(CustomObject customObject)
            {
                _objecten.Add(customObject);
            }
        }

        public class CustomObject
        {

            public string getString()
            {
                return "ik ben een string";
            }
        }

        static void Main(string[] args)
        {
            CustomObjectCollection _objs = new CustomObjectCollection { new CustomObject(), new CustomObject()}; //<-- neem maar e ven aan dat deze gewoon een lijst vult.

            IEnumerable<string> woei = from co in _objs select co.getString();
        
            foreach (string s in woei)
                Console.WriteLine(s);            
        }
    }
}


Zoals gezegd, ReSharper fixed alles :)

[ Voor 1% gewijzigd door Phyxion op 21-04-2011 10:43 . Reden: Wat opruiming :) ]

'You like a gay cowboy and you look like a gay terrorist.' - James May


Acties:
  • 0 Henk 'm!

  • BasieP
  • Registratie: Oktober 2000
  • Laatst online: 22-07-2024
oke wacht even.

Ik denk dat mijn testcase niet helemaal voldoet...

Ik heb hier een wat ingewikkeldere situatie staan, en dacht 'ik schrijf even op got een testcase uit'.
Echter moet deze dan natuurlijk wel dezelfde symptonen vertonen ;)

Ik ga ff een goede bouwen..

This message was sent on 100% recyclable electrons.


Acties:
  • 0 Henk 'm!

  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
BasieP schreef op donderdag 21 april 2011 @ 10:40:
Als ik over 'co' hover (ervoor) dan geeft hij aan dat deze van type 'CustomObjectCollection' is.
Ik had verwacht dat dit van type 'CustomObject' zou zijn.
Dan heb je denk ik niet exact de code gekopieerd, want de volgende code werkt bij mij allemaal perfect

C#:
1
2
3
4
5
var col = new CustomObjectCollection();

IEnumerable<string> result = from c in col select c.getString();
var result2 = from c in col select c.getString();
List<string> result3 = (from c in col select c.getString()).ToList();


edit:
Waarschijnlijk zit het probleem dus niet in je collectie maar in je Linq Query. Weet je zeker dat je wel van "co" selecteert en niet per ongeluk uit "_objs", die is immers van het type CustomObjectCollection.

[ Voor 14% gewijzigd door Woy op 21-04-2011 10:48 ]

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


Acties:
  • 0 Henk 'm!

  • BasieP
  • Registratie: Oktober 2000
  • Laatst online: 22-07-2024
Nou nog even gekeken, het probleem zit hem hier in:

Ik heb een query in een query, en daar gaat dus iets mis.

C#:
1
2
3
4
5
6
7
8
9
10
List<CustomObjectCollection> collections = new List<CustomObjectCollection>();
CustomObjectCollection _objs = new CustomObjectCollection() { 
    new CustomObject(),
    new CustomObject()
};

collections.Add(_objs);
collections.Add(_objs);

IEnumerable<string> woei = from co in (from _obj in collections select _obj) select co.getString();


is dus wel wat anders dan ik aan het begin dacht, maar evengoed een probleem.
Ik had gedacht dat het aan mijn custom collectie lag, en dat ik een of andere linq interface moest implementeren..

[ Voor 31% gewijzigd door BasieP op 21-04-2011 10:54 ]

This message was sent on 100% recyclable electrons.


Acties:
  • 0 Henk 'm!

  • Phyxion
  • Registratie: April 2004
  • Niet online

Phyxion

_/-\o_

.

[ Voor 100% gewijzigd door Phyxion op 21-04-2011 11:01 ]

'You like a gay cowboy and you look like a gay terrorist.' - James May


Acties:
  • 0 Henk 'm!

  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
C#:
1
2
3
4
5
6
7
8
9
10
List<CustomObjectCollection> collections = new List<CustomObjectCollection>();
CustomObjectCollection _objs = new CustomObjectCollection() { 
    new CustomObject(),
    new CustomObject()
};

collections.Add(_objs);
collections.Add(_objs);

IEnumerable<string> woei = from co in (from _obj in collections select _obj).First() select co.getString();

?
Je inner query levert immer weer een IEnumerable<CustomObjectCollection> op dus zal co logischerwijs ook een CustomObjectCollection zijn.

Of met SelectMany, om alle elementen weer te selecteren.

[ Voor 5% gewijzigd door Woy op 21-04-2011 11:02 ]

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


Acties:
  • 0 Henk 'm!

  • BasieP
  • Registratie: Oktober 2000
  • Laatst online: 22-07-2024
Mja.. das leuk, maar ik wil voor AL mijn elementen die getString aanroepen... niet alleen de eerste..
Daar is linq toch een beetje voor gemaakt?

Ik nap ook niet echt waarom
C#:
1
from _obj in collections select _obj

een element van type CustomObjectCollection oplevert, ipv een iteratie door een lijst met CustomObject

[ Voor 39% gewijzigd door BasieP op 21-04-2011 11:04 ]

This message was sent on 100% recyclable electrons.


Acties:
  • 0 Henk 'm!

  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
C#:
1
IEnumerable<string> woei = from co in (from _obj in collections select _obj).SelectMany(x=>x) select co.getString();

Met SelectMany kan je dat weer doen. Het is belangrijk dat je zorgt dat er de juiste input in gaat, anders krijg je natuurlijk ook niet de juiste output.

Overigens vraag ik me af of je de inner query wel echt nodig hebt. :?
BasieP schreef op donderdag 21 april 2011 @ 11:02:
een element van type CustomObjectCollection oplevert, ipv een iteratie door een lijst met CustomObject
Je selecteerd een x aantal CustomObjectCollections uit een IEnumerable<CustomObjectCollection>. Hoe moet C# weten dat jij de elementen uit de collection wil hebben, en niet de collections zelf?

[ Voor 47% gewijzigd door Woy op 21-04-2011 11:06 ]

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


Acties:
  • 0 Henk 'm!

  • BasieP
  • Registratie: Oktober 2000
  • Laatst online: 22-07-2024
M'kay laat maar..
De leesbaarheid is hier alweer af..

SelectMany(x=>x) :S wie verzint dat?

* BasieP gaat wel weer wat foreach lusjes in elkaar zetten

This message was sent on 100% recyclable electrons.


Acties:
  • 0 Henk 'm!

  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
Het is ook niet echt een gangbare constructie. Volgens mij wil jij gewoon een Union van je collecties uitvoeren, en daarna een Linq query erop uit voeren.

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


Acties:
  • 0 Henk 'm!

  • BasieP
  • Registratie: Oktober 2000
  • Laatst online: 22-07-2024
Woy schreef op donderdag 21 april 2011 @ 11:08:
Het is ook niet echt een gangbare constructie. Volgens mij wil jij gewoon een Union van je collecties uitvoeren, en daarna een Linq query erop uit voeren.
in mijn originele code is het als volgt:

ik heb een collectie1 met daarin objecten van type1
die type1 objecten hebben weer lijsten van collectie2
die hebben weer objecten met type2
die hebben op hun beurt een property.

als deze property gelijk is aan een waarde die ik al heb dan .....

in 'normale' code is dat dit:

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
foreach (Object1 o1 in Collectie)
{
    bool found = false;
    foreach (Object2 o2 in o1.Collectie)
        if (o2.Property == bekendewaarde)
        {
            doeIets();
            found = true;
            break;
        }
    if (found)
        break;
}

This message was sent on 100% recyclable electrons.


Acties:
  • 0 Henk 'm!

  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
Met een extension method kun je het overigens een stuk leesbaarder maken
C#:
1
2
3
4
5
6
7
8
9
public static class MyExtensions
{
    public static IEnumerable<T> Flatten<T>( this IEnumerable<IEnumerable<T>> collectionToFlatten)
    {
        return collectionToFlatten.SelectMany(col => col);
    }
}

IEnumerable<string> woei = from co in collections.Flatten() select co.getString();

o.i.d.

[ Voor 3% gewijzigd door Woy op 21-04-2011 11:17 ]

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


Acties:
  • 0 Henk 'm!

  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
BasieP schreef op donderdag 21 april 2011 @ 11:15:
[...]
ik heb een collectie1 met daarin objecten van type1
die type1 objecten hebben weer lijsten van collectie2
die hebben weer objecten met type2
die hebben op hun beurt een property.
Juist daarvoor kun je de SelectMany goed gebruiken!

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


Acties:
  • 0 Henk 'm!

  • RayNbow
  • Registratie: Maart 2003
  • Laatst online: 10:51

RayNbow

Kirika <3

BasieP schreef op donderdag 21 april 2011 @ 11:15:
[...]


in mijn originele code is het als volgt:

ik heb een collectie1 met daarin objecten van type1
die type1 objecten hebben weer lijsten van collectie2
die hebben weer objecten met type2
die hebben op hun beurt een property.

als deze property gelijk is aan een waarde die ik al heb dan .....

in 'normale' code is dat dit:

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
foreach (Object1 o1 in Collectie)
{
    bool found = false;
    foreach (Object2 o2 in o1.Collectie)
        if (o2.Property == bekendewaarde)
        {
            doeIets();
            found = true;
            break;
        }
    if (found)
        break;
}
Zoiets?
C#:
1
2
3
4
5
6
7
var x = (from o1 in Collectie
         from o2 in o1.Collectie
         where o2.Property == bekendewaarde
         select o2).FirstOrDefault();

if (x != null)
    doeIets(x);

(Niet getest met een compiler, dus er kunnen fouten in zitten :p)
BasieP schreef op donderdag 21 april 2011 @ 11:05:
SelectMany(x=>x) :S wie verzint dat?
Tja, in Haskell schrijven we gewoon (>>= id)... :+
(Eigenlijk schrijven we join in Haskell...)

[ Voor 12% gewijzigd door RayNbow op 21-04-2011 12:50 ]

Ipsa Scientia Potestas Est
NNID: ShinNoNoir


Acties:
  • 0 Henk 'm!

  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
@RayNbow: Dat is inderdaad makkelijker ( Al is het natuurlijk uiteindelijk eigenlijk hetzelfde als SelectMany ), ik zat te veel met het voorbeeld van de TS in mijn hoofd.

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


Acties:
  • 0 Henk 'm!

  • R4gnax
  • Registratie: Maart 2009
  • Laatst online: 06-09 17:51
Of zoiets:

C#:
1
2
3
4
5
Collectie
  .SelectMany(x => x.Collectie)
  .Where(x => x.Property == bekendeWaarde)
  .ToList()
  .ForEach(x => x.doeIets());



Ik heb trouwens nooit echt begrepen waarom ForEach standaard niet beschikbaar is voor IEnumerable<T>. Een foreach loop werkt tenslotte onder de kap ook op een IEnumerator<T>. Nu heb je in dit soort situaties weer een expliciete ToList operatie nodig...

Acties:
  • 0 Henk 'm!

Verwijderd

Ik weet niet of je deze custom collection nog op andere plekken gaat gebruiken dan zou ik deze middels generics nog even aanpassen zodat deze niet vast zit aan één object.

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class CustomObjectCollection<T> : IEnumerable, IEnumerable<T> 
        { 
            private List<T> _objecten = new List<T>(); 

            public IEnumerator GetEnumerator() 
            { 
                return (_objecten as IEnumerable).GetEnumerator(); 
            } 

            IEnumerator<T> IEnumerable<T>.GetEnumerator() 
            { 
                return (_objecten as IEnumerable<T>).GetEnumerator(); 
            } 

            public void Add(T obj) 
            { 
                _objecten.Add(obj); 
            } 
        } 


Zodat de constructie wordt.

C#:
1
List<CustomObjectCollection<CustomObject>> collections = new List<CustomObjectCollection<CustomObject>>();

Acties:
  • 0 Henk 'm!

  • BasieP
  • Registratie: Oktober 2000
  • Laatst online: 22-07-2024
Verwijderd schreef op donderdag 21 april 2011 @ 13:14:
Ik weet niet of je deze custom collection nog op andere plekken gaat gebruiken dan zou ik deze middels generics nog even aanpassen zodat deze niet vast zit aan één object.
collectie bevatte nog wat meer dan alleen mijn poc hier..
zodoende kan ie niet echt generiek gemaakt worden.

This message was sent on 100% recyclable electrons.


Acties:
  • 0 Henk 'm!

Verwijderd

BasieP schreef op donderdag 21 april 2011 @ 13:23:
[...]

collectie bevatte nog wat meer dan alleen mijn poc hier..
zodoende kan ie niet echt generiek gemaakt worden.
Oke dan houdt het op.

Acties:
  • 0 Henk 'm!

Verwijderd

R4gnax schreef op donderdag 21 april 2011 @ 13:13:
Ik heb trouwens nooit echt begrepen waarom ForEach standaard niet beschikbaar is voor IEnumerable<T>. Een foreach loop werkt tenslotte onder de kap ook op een IEnumerator<T>. Nu heb je in dit soort situaties weer een expliciete ToList operatie nodig...
Dit heeft volgens mij te maken met dat alle linq extension methods in principe geen side effects mogen hebben. Een ForEach is eigenlijk JUIST bedoeld om een bestaande collectie objecten allemaal te wijzigen zonder dat de collectie an sich verandert.

Acties:
  • 0 Henk 'm!

  • R4gnax
  • Registratie: Maart 2009
  • Laatst online: 06-09 17:51
Verwijderd schreef op donderdag 21 april 2011 @ 16:49:
[...]


Dit heeft volgens mij te maken met dat alle linq extension methods in principe geen side effects mogen hebben. Een ForEach is eigenlijk JUIST bedoeld om een bestaande collectie objecten allemaal te wijzigen zonder dat de collectie an sich verandert.
Akkoord met het side-effects argument. Maar; een method op IEnumerable<T>, die zich hetzelfde gedraagd als IList<T>.ForEach of als Array.ForEach<T>, hoeft toch niet per definitie iets met de LINQ familie van methods te maken te hebben?

Acties:
  • 0 Henk 'm!

  • cfern
  • Registratie: Oktober 2009
  • Laatst online: 09:37
Hier een blog post van een Microsoftmeneer over waarom .ForEach(x=>blaat) voorlopig niet in LINQ zal verschijnen.

Stukje trivia voor wie het iets kan schelen: foreach vereist niet eens dat je verzameling IEnumerable of een IEnumerable<T> implementeert. Het enige wat foreach nodig heeft is een doorloopbaar object met een public method GetEnumerator() die een IEnumerator ophoest. Voorbeeld:

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class CountCountEnumerator : IEnumerator
{
    private int _current;

    public bool MoveNext()
    {
        if (_current >= 3) return false;
        _current++;
        return true;
    }

    public void Reset()
    {
        _current = 1;
    }

    public object Current
    {
        get { return _current; }
    }
}

class GraafTel
{
    public IEnumerator GetEnumerator()
    {
        return new CountCountEnumerator();
    }
}


En dan dus

C#:
1
foreach(int number in new GraafTel()) Console.Write(number + " ");

"I'd rather have a full bottle in front of me, than a full frontal lobotomy." (Tom Waits) | PoE

Pagina: 1