Reputation: 508
To begin with, I have two projects under the names of Components
and WindowsFormsApplication5
as part of my solution (there are more, but those are irrelevant). The dictionaries I am referring to are in Components
, defined as follows, along-with their properties -:
Dictionary<int, ButtonBase> RespMCQ, DispMCQ; //<Question Number, Checkbox/Radiobutton>
Dictionary<int, List<ButtonBase>> RespMSQ, DispMSQ;
public Dictionary<int, ButtonBase> MCQResp
{
get
{
return RespMCQ;
}
set
{
RespMCQ = value;
}
}
public Dictionary<int, List<ButtonBase>> MSQResponse
{
get
{
return RespMSQ;
}
set
{
RespMSQ = value;
}
}
public Dictionary<int, ButtonBase> MCQDisplay
{
get
{
return DispMCQ;
}
set
{
DispMCQ = value;
}
}
public Dictionary<int, List<ButtonBase>> MSQDisplay
{
get
{
return DispMSQ;
}
set
{
DispMSQ = value;
}
}
The dictionaries are instantiated in the constructor of QuestionDisplay.cs
, which is part of the Components
project (this is where they reside). This is shown as follows -:
public QuestionDisplay()
{
InitializeComponent();
RespMCQ = new Dictionary<int, ButtonBase>();
DispMCQ = new Dictionary<int, ButtonBase>();
RespMSQ = new Dictionary<int, List<ButtonBase>>();
DispMSQ = new Dictionary<int, List<ButtonBase>>();
....
}
In Form1.cs
of WindowsFormsApplication5
(I did not choose this name, pardon me if it sounds bland), I am using the properties I mentioned. There are no namespace issues and compilation occurs as normal.
During the Form_Load
of Form1
, however, when I refer to these properties, they throw a NullReferenceException
saying -:
Object reference not set to an instance of an object.
The first occurrence of these properties is in a method called WorkFlowPanel(string S, QuestionContainerState obj)
which is called at the time of form load -:
if (QuesTypes[i].Equals("MCQ"))
{
string text = "";
for (int ansCount = 0; ansCount < questions[i].Options.Count(); ansCount++)
{
if (Convert.ToInt32(K.Key).Equals(ansCount + 1))
{
text = questions[i].Options[ansCount];
break;
}
}
questionDisplay1.MCQResp.Add(i, new CustomRadio { optionId = Convert.ToInt32(K.Key), Checked = true, Text = text }); //Using anonymous class
}
else if (QuesTypes[i].Equals("MSQ") || QuesTypes[i].Equals("Linked"))
{
var storeoptions = K.Key.Split(':');
questionDisplay1.MSQResponse.Add(i, new List<ButtonBase>());
for (int s = 0; s < storeoptions.Count(); s++)
{
questionDisplay1.MSQResponse[i].Add(new CustomChecks { optionId = Convert.ToInt32(storeoptions[s]), Checked = true, Text = questions[i].Options[Convert.ToInt32(storeoptions[s])] });
}
}
To my surprise, on debugging, I found that the properties are always null despite the dictionaries being instantiated. Now this problem has a bit of a history which I will have to delve into, to provide context.
I was earlier battling with a Could not find a type for a name. The type was System.Collections.Dictionary...
bug which was occurring in Form1.resx
(I cannot recall the entire error). Apparently the dictionary properties were being serialized into binary format and encoded as base64 in the resx file, as follows -:
<data name="MCQDisplay" mimetype="application/x-microsoft.net.object.binary.base64">
<value>
AAEAAAD/////AQAAAAAAAAAEAQAA....
</value>
<data name="MSQDisplay" .....>
....
....
I followed a solution I found on Stackoverflow, which told me to copy the code back into an older version. This is the fix I had been applying since then. I found that if I removed these entries (which the compiler error asked me to do), the dictionary properties would always be null. I feel this is tied to my current scenario. It so happened that during the copy-back operation, the resx file entries somehow vanished.
Since then, it has never thrown this compiler error. I tried everything, including the use of ResXResourceWriter
to regenerate the entries. It's like they're not even acknowledged, and instead of the error, I'm told during run-time that the dictionary properties are always null! One person on IRC suggested that the error used to occur because ButtonBase
is not serializable, even though Dictionary
in itself is. Well, I never asked the compiler to serialize the dictionaries. I wonder why that even happened.
I would be really grateful if anyone could help me resolve this damning bug. I also had this vague solution in my head that instead of storing ButtonBase
children as values, I could store class objects instead which have properties mirroring the text, optionId and checked properties of the ButtonBase
child. However, I am thinking of this as a last resort.
EDIT_1: I don't know if this is relevant, but I found these entries in Form1.Designer.cs
-:
this.questionDisplay1.MCQDisplay = ((System.Collections.Generic.Dictionary<int, System.Windows.Forms.ButtonBase>)(resources.GetObject("questionDisplay1.MCQDisplay")));
this.questionDisplay1.MCQResp = ((System.Collections.Generic.Dictionary<int, System.Windows.Forms.ButtonBase>)(resources.GetObject("questionDisplay1.MCQResp")));
this.questionDisplay1.MSQDisplay = ((System.Collections.Generic.Dictionary<int, System.Collections.Generic.List<System.Windows.Forms.ButtonBase>>)(resources.GetObject("questionDisplay1.MSQDisplay")));
this.questionDisplay1.MSQResponse = ((System.Collections.Generic.Dictionary<int, System.Collections.Generic.List<System.Windows.Forms.ButtonBase>>)(resources.GetObject("questionDisplay1.MSQResponse")));
Do I have to remove these or modify them in some way? I feel these are somehow related to Matthew's answer.
EDIT_2: I managed to solve this problem by applying the following -:
Form1.Designer.cs
, the ones that I mentioned above.I am accepting Matthew's answer because I feel the non-serialization fix is relevant.
Upvotes: 3
Views: 1011
Reputation: 109567
Are these properties both public and members of a class derived from Form
, UserControl
or Control
?
If so, it is possible that the Forms designer will be trying to serialize the properties to the form's Designer.cs file. (In fact, from what you said, it's entirely probable.)
To stop that from happening you can use the DesignerSerializationVisibilityAttribute
to prevent it, for example:
[
Browsable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
]
public MyType MyPropertyWhichMustNotBeSerialized
{
get;
set;
}
You'll have to edit away any bad stuff it's already put in the ".Designer.cs" file though - adding this attribute might not make it go away automatically.
If you haven't regularly checking if you need to add this attribute, I think you might want to go through all your public properties in any classes derived directly or indirectly from Control
, and add this attribute where necessary.
For the problem will the null references; is it possible that FormLoad()
is being called as a side-effect of the call to InitialiseComponent()
? That would cause a null reference error.
To be safest you should use backing fields for your properties instead of automatic properties.
Then instead of initialising the fields in a constructor, initialise them right where you declare the fields, like this:
private Dictionary<int, ButtonBase> respMCQ = new Dictionary<int, ButtonBase>();
private Dictionary<int, ButtonBase> dispMCQ = new Dictionary<int, ButtonBase>();
private Dictionary<int, List<ButtonBase>> respMSQ = new Dictionary<int, List<ButtonBase>>();
private Dictionary<int, List<ButtonBase>> dispMSQ = new Dictionary<int, List<ButtonBase>>();
That way they are guaranteed to be initialised before any constructor is called.
Upvotes: 1