webForumDet fria alternativet

List<t>.Contains() returnerar fel värde för mig

.NET

5 svar · 450 visningar · startad av desdecado

Medlem sedan juli 2004206 inlägg
Frågan#1

Har lite problem med en List<t> som jag inte kan kolla om den innehåller ett object; jag får alltid False....
Har lite trimmad kod nedan där jag har en klass som heter UserCredentials och en av dess properties är AccessFunctions som är en lista beståendes av object av typen AccessFunction.
Det är metoden CanAccessFunction där man skickar in en enum (AccessFunction.AccessFunctionName) som ska returnera true eller false beroende på om den enumen är satt på en befintlig AccessFunction i listan.

Ser ni någo fel?
(På den bortkommenterade raden har jag testat att direkt lägga till ett object och sedan kolla med .Contains() och det fungerar. Dock så fungerar det inte om jag sätter en AccessFunction utanför klassen. Ja, de lagras ju i listan men jag får ingen träff...) §jr

public class UserCredentials
	{
	public List<AccessFunction> AccessFunctions = new List<AccessFunction>();
	public bool CanAccessFunction(AccessFunction.AccessFunctionName AccessfunctionName)
		{
		AccessFunction tmp = new AccessFunction();
		tmp.AccessfunctionName = AccessfunctionName;
		//AccessFunctions.Add(tmp);
		return = AccessFunctions.Contains(tmp);			             }
            }
		
public class AccessFunction
	{
	public AccessFunctionName AccessfunctionName = AccessFunctionName.User_Reader;
	public enum AccessFunctionName
		{
		Admin_Statistics = 10,
		Admin_SourceImages = 30,
	             Admin_Transactions = 40,
		User_Reader = 50,
		User_Writer = 60,
		Admin_User = 70,
		Admin_AccessGroups = 80,
		Admin_ErrorReciever = 90,
		Admin_Confirmation = 100
		}
	}
Medlem sedan maj 20012 812 inlägg
#2

AccessFunction tmp = new AccessFunction();
tmp.AccessfunctionName = AccessfunctionName;
//AccessFunctions.Add(tmp);
return = AccessFunctions.Contains(tmp);

Om jag inte är helt fel på det så kollar contains om ditt objekt redan finns i listan. I ditt fall ovan så kommer objektet ALDRIG att finnas i listan eftersom du skapar ett nytt varje gång som du kör din kod.

I fallet där du tar bort kommentarerna och kör med .ADD() metoden så finns ju ditt objekt med i listan, eftersom du lagt ditt det.

Det viktiga här att komma ihåg är att även om 2 objekt har exakt samma data så är det INTE samma objekt och du kommer inte få true tillbaka. Det måste vara samma instans av din klass som du skickar in. Alltså kommer ovanståend kod alltid returnera false.

- M

Medlem sedan dec. 19996 721 inlägg
#3

Det viktiga i Contains är inte att det är samma instans, utan att Equals returner true och GetHashCode returnerar samma sak för de jämförda instanserna.

I det här fallet känns det dock inte som att enum-värdet egentligen identifierar instansen, utan att det kan finnas flera instanser i listan med samma enum.

Loopa igenom listan och jämför.

Medlem sedan juli 2004206 inlägg
#4

Tack för svaren!

Gladh: ja, jag skapade just tmp objectet för att få en "kopia" av det objekt jag ville hitta. Att jag testade att Add'a det var bara en idiotkontroll av mig för att se så saker och ting fungerade. När jag debuggar så är ju listan fylld vid det tillfälle jag vill köra Contains och jag "ser" att ett likadant objekt ligger där redan som jag försöker att kolla av.

emission: Listan innehåller inga dubletter då den läses in från en storedproc (med DISTINCT i selectsatsen) och den skapas/läses in endast när man loggar in i min applikation (lagras i sessionobjectet och läses in i varje sidas/kontrolls basklass vid OnPageInit)

Visst kan jag loopa igenom listan för att se om jag får en träff i CanAccessFunction(), men är det det smidigaste sättet? Jag vill använda enums för koden blir mer lättläst och lättunderhållen när jag på sidor och i kontroller ska kolla behörighet för diverse olika saker.

Medlem sedan dec. 19996 721 inlägg
#5

Okej, till att börja med - är det hela AccessFunction som visas? I så fall är det lite onödigt att göra en hel klass vars enda syfte är att wrappa in en enum. Om din UserCredentials-klass i stället hade en List<AccessFunctionName> så skulle det bli lite enklare.

Om du vill hitta likadana värden i en List, baserat på hur objektet är konfigurerat och inte på vilken instans det är, så måste du overrida Equals och GetHashCode.

public class AccessFunction
    {
        public AccessFunctionName AccessfunctionName = AccessFunctionName.User_Reader;
        public enum AccessFunctionName
        {
            Admin_Statistics = 10,
            Admin_SourceImages = 30,
            Admin_Transactions = 40,
            User_Reader = 50,
            User_Writer = 60,
            Admin_User = 70,
            Admin_AccessGroups = 80,
            Admin_ErrorReciever = 90,
            Admin_Confirmation = 100
        }

        public override bool Equals(object obj)
        {
            AccessFunction other = obj as AccessFunction;
            if (other==null)
                return false;
            return other.AccessfunctionName.Equals(this.AccessfunctionName);
        }

        public override int GetHashCode()
        {
            return AccessfunctionName.GetHashCode();
        }

    }

Slutligen, om antalet AccessFunctionName kommer att bestå (vilket det bör, eftersom det är definierat som en enum), så kan du slå ihop alla till ett enda värde som du kontrollerar med bit-jämförelse.

//Värdetilldelningen kan göras automatiskt med [Flags]-attributet, men för tydlighets skull så gör vi tilldeningen "manuellt"
public enum AccessFunctionName
        {
            Admin_Statistics = 1,
            Admin_SourceImages = 2,
            Admin_Transactions = 4,
            User_Reader = 8,
            User_Writer = 16,
            Admin_User = 32,
            Admin_AccessGroups = 64,
            Admin_ErrorReciever = 128,
            Admin_Confirmation = 256
        }

och

public bool CanAccessFunction(AccessFunctionName function)
{
    return (AccessfunctionName & function) == function;
}

...vilket är ett vanligt och högpresterande sätt att lagra enums. Inte nödvändigtvis rätt i ditt fall, men det finns där om du är sugen.

Medlem sedan juli 2004206 inlägg
#6

Tack emission, det fungerade om jag körde en enum direkt i listan istället för en klass (ja, klassen innehöll fler properties förut men de behövs inte längre, bara dumt av mig att behålla den som klass).

Ja, att göra bit jämförelse har jag kört i tidigare projekt men det lämpar sig inte riktigt här (men jag håller med, det är smidigt och så behöver man ju bara släpa på en integer i sessionen)

Tackar och bockar än en gång! :birp

257 ms totalt · 4 externa anrop · v20260731065814-full.6fe65c25
121 ms — deklarationer (db)
0 ms — hämta statistik (cache)
134 ms — hämta tråd, inlägg och bilagor (db)
121 ms — ändringar (db)