0

I created the BloodType struct fnd dictionary which contains BloodType and BloodTyple list with acceptable blood. When i run my code:System.ArgumentException: "An item with the same key has already been added. Key: BloodTypeCheck.BloodType" at line 20

struct BloodType
{
}

BloodType Op;
BloodType Om;
BloodType Bp;
BloodType Bm;
BloodType Ap;
BloodType Am;
BloodType ABp;
BloodType ABm;
Dictionary<BloodType, List<BloodType>> dic=new Dictionary<BloodType, List<BloodType>>();
dic.Add(ABp, new List<BloodType> { Op, Om, Bp, Bm, Ap, Am, ABp, ABm });
dic.Add(ABm, new List<BloodType> { Om, Bm, Am, ABm });//at this line something wrong
dic.Add(Ap, new List<BloodType> { Om, Op, Am, Ap });
dic.Add(Am, new List<BloodType> { Om, Am, });
dic.Add(Bp, new List<BloodType> { Om, Op, Bm, Bp });
dic.Add(Bm, new List<BloodType> { Om, Bm });
dic.Add(Op, new List<BloodType> { Om, Op });
dic.Add(Om, new List<BloodType> { Om });

var list = new List<BloodType>();
list = GetTrueBloodType(Op, dic);

foreach (var item in list)
        {
            Console.WriteLine(item);
        }

Also i have a method that have to(if it'll work well) return List of acceptable blood type shown below:

public static List<BloodType> GetTrueBloodType(BloodType first, Dictionary<BloodType, List<BloodType>> dic)
        {
            return dic[first];
        }
bodkia22
  • 71
  • 8
  • 2
    The code above does not compile. Please post a [minimal, reproducible, verifiable example](https://stackoverflow.com/help/minimal-reproducible-example). You can use https://dotnetfiddle.net/ for it. – Markus Deibel Sep 29 '19 at 16:23
  • 1
    Anything that acts as a key into a directory should have a good GetHashCode() and Equals() implementation. Right now two *values* of type BloodType are indistinguishable. Quick fix is to make them objects instead of values, change the BloodType declaration from struct to class. – Hans Passant Sep 29 '19 at 16:23
  • 3
    Are you sure you want a BloodType *struct* and not an *enum*? – Hans Kesting Sep 29 '19 at 16:33
  • The error message is self-explanatory. The marked duplicate, along with other similar questions, repeats this explanation and provides more details. You are using the exact same key value for each attempt to add to the dictionary. Note that sorting the same value in two or more differently-named variables **does not make a new value**. The variables just all wind up with the same value. – Peter Duniho Sep 29 '19 at 17:24
  • I disagree: it is not a duplicate... it is a strange behavior of the compiler when a struct have no members and it allows using unassigned vars... and compile... –  Sep 29 '19 at 17:57

2 Answers2

1

First of all I would change the struct to a class as @HansPassant suggested in the comments above. In the BloodType class I added a property called name so that you can write some data to the console with the foreach loop and I also added a constructor so you can add the data to the class when initializing the object.

class BloodType
{
    public BloodType()//default constructor
    {

    }

    public BloodType(string name)//constructor
    {
        this.name = name;
    }

    public string name { get; set; }

    public static List<BloodType> GetTrueBloodType(BloodType first, Dictionary<BloodType, List<BloodType>> dic)
    {
        return dic[first];
    }
}

The problem with your code is that you declared the BloodType Variables, but you didn't initialize them :

BloodType Op = new BloodType("Op");
BloodType Om = new BloodType("Om");
BloodType Bp = new BloodType("Bp");
BloodType Bm = new BloodType("Bm");
BloodType Ap = new BloodType("Ap");
BloodType Am = new BloodType("Am");
BloodType ABp = new BloodType("ABp");
BloodType ABm = new BloodType("ABm");

Dictionary<BloodType, List<BloodType>> dic = new Dictionary<BloodType, List<BloodType>>();
dic.Add(ABp, new List<BloodType> { Op, Om, Bp, Bm, Ap, Am, ABp, ABm });
dic.Add(ABm, new List<BloodType> { Om, Bm, Am, ABm });
dic.Add(Ap, new List<BloodType> { Om, Op, Am, Ap });
dic.Add(Am, new List<BloodType> { Om, Am, });
dic.Add(Bp, new List<BloodType> { Om, Op, Bm, Bp });
dic.Add(Bm, new List<BloodType> { Om, Bm });
dic.Add(Op, new List<BloodType> { Om, Op });
dic.Add(Om, new List<BloodType> { Om });

List<BloodType> list = BloodType.GetTrueBloodType(Op, dic);

foreach (var item in list)
{
    Console.WriteLine(item.name);
}

Output :

Om

Op

Slaven Tojić
  • 2,945
  • 2
  • 14
  • 33
0

It is because your struct is empty, there is no members.

It seems that the dictionary considers they are the same, like if null.

And Key of Dictionary must be unique.

I notice that the compiler doesn't throw a non assignment error... that it throws if the struct have members...

For the type of blood you can use an enum like that, no need to create a struct or a class that are for encapsulating data structure, because enum are enumarated integers values (like parameters or lists of states):

public enum BlooType
{
  O,
  A,
  B,
  AB
}

Here O = 0, A = 1, B = 2 and AB = 3

Perhaps you need that if I understand, to made the thing simple:

public enum BlooType
{
  Op,  // = 0
  Om,
  Bp,
  Bm,
  Ap,
  Am,
  ABp,
  ABm  // = 7
}

And rhesus :

public enum Rhesus
{
  Positive,
  Negative
}

Here an explanation of what enum are and how to use them:

https://www.tutorialspoint.com/csharp/csharp_enums.htm

Now you can write:

dic.Add(BloodType.ABp, new List<BloodType> { BloodType.Op, BloodType.Om ... });

To get a readable string of a blood type you can write for example:

string strType = Enum.GetName(BlooType.Op);

Or:

string strType = Enum.GetName(dic[typeBlood][0]);

Here an IL explanation of the exception.

With empty struct:

struct A
{
}

static void Main(string[] args)
{
  A value1;
  A value2;
  var list = new List<A>();
  list.Add(value1);
  list.Add(value2);
}

IL code generated:

.method private hidebysig static 
  void Main (
    string[] args
  ) cil managed 
{
  // Method begins at RVA 0x2050
  // Code size 24 (0x18)
  .maxstack 2
  .entrypoint
  .locals init (
    [0] valuetype ConsoleApp1.Program/A value1,
    [1] valuetype ConsoleApp1.Program/A value2,
    [2] class [mscorlib]System.Collections.Generic.List`1<valuetype ConsoleApp1.Program/A> list
  )

  // (no C# code)
  IL_0000: nop
  // List<A> list = new List<A>();
  IL_0001: newobj instance void class [mscorlib]System.Collections.Generic.List`1<valuetype ConsoleApp1.Program/A>::.ctor()
  IL_0006: stloc.2
  // list.Add(item);
  IL_0007: ldloc.2
  IL_0008: ldloc.0
  IL_0009: callvirt instance void class [mscorlib]System.Collections.Generic.List`1<valuetype ConsoleApp1.Program/A>::Add(!0)
  // (no C# code)
  IL_000e: nop
  // list.Add(item2);
  IL_000f: ldloc.2
  IL_0010: ldloc.1
  IL_0011: callvirt instance void class [mscorlib]System.Collections.Generic.List`1<valuetype ConsoleApp1.Program/A>::Add(!0)
  // (no C# code)
  IL_0016: nop
  // }
  IL_0017: ret
} // end of method Program::Main

With non empty struct and assignment:

struct A
{
  public int v;
}

static void Main(string[] args)
{
  A value1 = new A();
  A value2 = new A(); 
  var list = new List<A>();
  list.Add(value1);
  list.Add(value2);
}

IL code generated:

.method private hidebysig static 
  void Main (
    string[] args
  ) cil managed 
{
  // Method begins at RVA 0x2050
  // Code size 40 (0x28)
  .maxstack 2
  .entrypoint
  .locals init (
    [0] valuetype ConsoleApp1.Program/A value1,
    [1] valuetype ConsoleApp1.Program/A value2,
    [2] class [mscorlib]System.Collections.Generic.List`1<valuetype ConsoleApp1.Program/A> list
  )

  // (no C# code)
  IL_0000: nop
  IL_0001: ldloca.s 0
  // A item = default(A);
  IL_0003: initobj ConsoleApp1.Program/A
  // (no C# code)
  IL_0009: ldloca.s 1
  // A item2 = default(A);
  IL_000b: initobj ConsoleApp1.Program/A
  // List<A> list = new List<A>();
  IL_0011: newobj instance void class [mscorlib]System.Collections.Generic.List`1<valuetype ConsoleApp1.Program/A>::.ctor()
  IL_0016: stloc.2
  // list.Add(item);
  IL_0017: ldloc.2
  IL_0018: ldloc.0
  IL_0019: callvirt instance void class [mscorlib]System.Collections.Generic.List`1<valuetype ConsoleApp1.Program/A>::Add(!0)
  // (no C# code)
  IL_001e: nop
  // list.Add(item2);
  IL_001f: ldloc.2
  IL_0020: ldloc.1
  IL_0021: callvirt instance void class [mscorlib]System.Collections.Generic.List`1<valuetype ConsoleApp1.Program/A>::Add(!0)
  // (no C# code)
  IL_0026: nop
  // }
  IL_0027: ret
} // end of method Program::Main

When the struct have no members and vars are not assigned, this code is missing:

IL_0001: ldloca.s 0
// A item = default(A);
IL_0003: initobj ConsoleApp1.Program/A
// (no C# code)
IL_0009: ldloca.s 1
// A item2 = default(A);
IL_000b: initobj ConsoleApp1.Program/A

I'm not involved in IL but it seems that when the struct is empty, the compiler does not throw an error and uses the variables of struct type as references (memory contains 0 i.e. null).

  • Implement the static method returning the acceptable onor blood types for the blood type specifield as a parametr. Create a dictionary where key is a blood type of a recipient and the value is a list of compatible blood types(like my owm). Blood type should be defined by the bloodType struct, it's a task. – bodkia22 Sep 29 '19 at 17:12
  • I don't really understand but I updated the answer for the `dic.Add()` usage. –  Sep 29 '19 at 17:15