Object name reference not going out of scope - c#

I've spotted a very confusing behavior:
I have two for blocks. I declare two distinct objects with the same name newDevice inside each one. I expect that the first object goes out of scope when the first for block ends, and a for a new object to be created in the second for block.
What actually happens is that the initial value is never "released" from the variable name. The variable name still references the first object.
The compiler doesn't complain about already declared object, and if I don't use the var keyword in the second attribution, a "cannot resolve symbol" error arises.
Here's the simplified code:
/* First "for" loop */
var masters = networkDiscoveryCompletedEventArgs.ConnectionInfos.Where(x => x.DataModelHandshake.DeviceId == masterId).ToList();
foreach (var connectionInfoMaster in masters)
{
// First declaration. newDevice's type, after construction, is "MasterDevice".
var newDevice = new MasterDevice
{
DeviceName = connectionInfoMaster.DataModelHandshake.Name,
DeviceId = connectionInfoMaster.DataModelHandshake.DeviceId,
ConnectionInfo = connectionInfoMaster
};
MasterDevices.Add(newDevice);
}
/* Second "for" loop */
var slaves = networkDiscoveryCompletedEventArgs.ConnectionInfos.Where(x => x.DataModelHandshake.DeviceId != masterId).ToList();
foreach (var connectionInfoSlave in slaves)
{
// Second declaration. Inspecting newDevice in debbug mode,
// it's not undeclared at this point. Instead, it retains
// the previous object reference ("MasterDevice"). newDevice's type, after construction,
// is also "MasterDevice".
var newDevice = new SlaveDevice
{
DeviceName = connectionInfoSlave.DataModelHandshake.Name,
DeviceId = connectionInfoSlave.DataModelHandshake.DeviceId,
ConnectionInfo = connectionInfoSlave
};
SlaveDevices.Add(newDevice);
}
Using different names solves the issue.
Am I missing something, or is it a compiler bug?
This never happened before, and I'm really confused.

Related

What happens when you change property parameter in a method without ref (C#) [duplicate]

This question already has answers here:
When to use ref and when it is not necessary in C#
(9 answers)
Closed 1 year ago.
I am working with some Legacy code. I general method that I usually would call like this:
Poco = MyMethod(Poco.Id) // Lookup PocoToReturn on Poco.Id and return PocoToReturn
are called in my Legacy like this:
MyLegacyMethod(Poco) // Lookup lookedUpPoco on Poco.Id, set Poco = lookedUpPoco and return (void)
The later work mostly but not always. I am struggling to understand when the later work and where it does not works (and should be fixed)
Consider the following execution of the code I are located at the end of this post:
Facade.AppleFacade.GetApple() is called
Persist.ApplePersist.GetAppleInfo(appleInfo) is called
info = new AppleInfo { Id = "newId", Name = "Test"} is executed
GetAppleInfo in the persist returns to GetApple in the facade
Expected: Info.Id = "newId" and Info Name = "test"
Actual: Info.Id = "oldId" and Info Name = null
If I add ref to get MyStrangeMethod(ref Poco) the my Actual will be as Expected. I guess
I have 2-3 Questions:
Why is ref neccessary in this case then other code wihtout ref is working without problems?
In general, what is the difference between using ref and using no prefix for Objects of different type?
(I think I know the answer to this) Why do I calls like MyMethod(ref myPoco.myProperty) result in compile time error A property or indexer may not be passed as an out or ref parameter
Below the code example I mention above
namespace Facade
{
public class AppleFacade
{
public AppleInfo GetApple()
{
var appleInfo = new AppleInfo();
appleInfo.Id = "oldId";
_applePersist.LoadAppleInfo(appleInfo);
return appleInfo;
}
}
}
namespace Persist
{
public class ApplePersist
{
public void GetAppleInfo(AppleInfo info)
{
info = new AppleInfo
{
Id = "id",
Name = "test"
};
}
}
}
In C# class instances are reference types and structs are value type (primitive types are value type too).
A variable of a reference type does not contain its data directly; it contains a reference to its data.
When you pass a reference type to a method you pass its reference value.
So, in GetAppleInfo you receive a new reference for the appleInfo object and you simple overwrite that with a new reference of the new AppleInfo instance.
This clearly does not change anything in the caller.
Your example is analogous to this:
AppleInfo appleInfo = new AppleInfo{ Name = "A"};
AppleInfo info = appleInfo;
info = new AppleInfo{ Name = "B"};
// appleInfo.Name == "A"
If you pass a reference type by reference using ref (or out) then you are passing its reference by reference instead of value. This would be analogue to this:
AppleInfo a = new AppleInfo{ Name = "A"};
a = new AppleInfo{ Name = "B"};
// a.Name == "B"

foreach looping variable throws NullReferenceException but Enumerable is not null

Some people decided to close my previous question, but the question they linked (What is a NullReferenceException, and how do I fix it?) did not have an answer. This question is fundamentally different since the enumerable is populated. It is not null. Just as the first answer stated, I placed "strategic breakpoints" and checked the variables.
I'm debugging a XUnit test and it turns out that in my business logic the iteration variable in the foreach loop is throws an exception "Object Reference not set to instance of object". However, the list over which the iteration is happening is NOT null. I can see that when I'm debugging. Here is the code:
Business logic:
List<string> regionArray = new List<string>();
if (someCondition)
{
regionArray = _utils.GetRegions(someParam); // this is not returning null
}
foreach (var region in regionArray)
{
var query = from dataSet in myDataSets
where dataSet.Location == region
select dataSet;
var queryResult = query.FirstOrDefault();
if (queryResult == null)
{
// do stuff
} else if (queryResult.State != States.Provisioned)
{
// do stuff
}
}
Here is how I am mocking the _utils.GetRegions call, but I dont think thats the problem.
private Mock<IUtils> _mockRegionUtils;
[Fact]
public void ItWorks()
{
// do stuff
_mockRegionUtils = new Mock<IUtils>();
_mockRegionUtils.Setup(utils => utils.GetRegions(It.IsAny<ISomeParam>())).Returns(new List<string>() {"america", "china"});
// call business logic
}
I have checked all the types in the debugger. regionArray.GetType() returns {System.Collections.Generic.List`1[System.String]}. when I type region into the console however, i get:
region
'region' threw an exception of type 'System.NullReferenceException'
how is this possible?
EDIT: fixed a typo above, sorry about that. Something weird though, so if I reassign the value of regionArray to be an inline list, it still fails. But if I define a new inline list and iterate over that, the looping works fine.
List<string> regionArray = new List<string>();
if (someCondition)
{
regionArray = _utils.GetRegions(someParam); // this is not returning null
}
regionArray = new List<string>() {"china", "america"};
List<string> temp = new List<string>() {"foo", "bar"}
foreach (var region in regionArray)
{
// region still throws null reference exception
foreach (var tempVar in temp)
{
var c = tempVar; // this works. tempvar is never null.
}
var query = from dataSet in myDataSets
where dataSet.Location == region
select dataSet;
var queryResult = query.FirstOrDefault();
if (queryResult == null)
{
// do stuff
} else if (queryResult.State != States.Provisioned)
{
// do stuff
}
}
EDIT 2: So I tried iterating over the regionArray in the same way just before the logic above, and it worked fine.
List<string> regionArray = new List<string>();
if (someCondition)
{
regionArray = _utils.GetRegions(someParam); // this is not returning null
}
foreach (var region in regionArray)
{
var c = region; // this works
}
foreach (var region in regionArray)
{
// region throws null reference exception
var query = from dataSet in myDataSets
where dataSet.Location == region
select dataSet;
var queryResult = query.FirstOrDefault();
if (queryResult == null)
{
// do stuff
} else if (queryResult.State != States.Provisioned)
{
// do stuff
}
}
so most likely, it is not a problem with the moq object. based on #Iliar's suggestion, I will see if regionArray gets modified, but at first glance since regionArray is not used within the loop, my answer would be "no".
Update: I got around this issue by renaming the region looping variable to a different name. As it turns out, I was doing another foreach (var region ...) loop earlier in my code. I spoke to some senior colleagues as to why these 2 names would conflict with each other, and they said maybe it was some issue with symbols in VSCode and not really with my actual code. Thank you all for your help!
There was a lot of info in this thread, so just to summarize here are a few bulletpoints in case it is helpful to someone else in the future:
When debugging an XUnit test, I was seeing my looping variable in my foreach displaying the following info in the tooltip 'region' threw an exception of type 'System.NullReferenceException' Data [IDictionary]:{System.Collections.ListDictionaryInternal} HResult [int]:-2147467261 HelpLink [string]:null InnerException [Exception]:null Message [string]:"Object reference not set to an instance of an object." Source [string]:"9dd66c33104045bba27ad3fc9fb95185" StackTrace [string]:" at <>x.<>m0(<IngestEvents>d__13 <>4__this)" TargetSite [MethodBase]:{System.String <>m0(<IngestEvents>d__13)} Static members ....
even as I stepped INTO the loop, the tooltip for region was still showing the above, and when I typed region into the console, I got 'region' threw an exception of type 'System.NullReferenceException'.
The above 2 points led me to believe region was null. However, through #IVSoftware 's help, I verified that region was not actually null, because the assertion was passing.
I then looked at the rest of my code, and as a random guess, I tried renaming the looping variable region to something else. When I did, region was correctly set to the elements of the list.
Hi I really hope to be helpful. First I will answer your question "how is this possible?" and I think I can explain why your edited question with the inline list works. True, the GetRegions method returns a list that is not null. Sure, if you call GetType() on this it correctly identifies it as a "list of strings". I believe however, that the GetRegions method is returning a list that contains at least one null value. And you prove it out yourself when you added the edit, because you say this works:
regionArray = new List<string>() {"china", "america"};
But try making a list with three values like this where one of them is null and probably the loop will fail again.
regionArray = new List<string>() {"china", null, "america"};
This suggests a bug inside the GetRegions method is putting a null value into the list that it is returning. Adding these two lines at the beginning of your loop might go a long way to identifying the issue:
foreach (var region in regionArray)
{
// Look for this in the Visual Studio 'Output' window
System.Diagnostics.Debug.WriteLine(region == null ? "Null" : region);
// You believe that region is not null. So 'assert' that
// this evaluates to True and if it doesn't the debugger will break here.
System.Diagnostics.Debug.Assert(region != null, "Break on this line if region is null");
...
From what I can tell, I would look inside your GetRegions(someParam) method and see if it's inserting a null into the list somewhere. Good luck!
List<string> regionArray = new List<string>();
if (someCondition)
{
regionArray = _utils.GetRegions(someParam); // this is not returning null
}
this will override the regionArray instance, to the GetRegions instance, so creating new List<string> instance is useless.
What you want is :
List<string> regionArray = new List<string>();
if (someCondition)
{
regionArray.AddRange(_utils.GetRegions(someParam));
}
which is using this new instance, and add the resulted elements inside it.
If _utils.GetRegions(someParam) returns empty set, then regionArray will not be null, but it'll be empty regionArray.Count == 0.
this is also can be done using ToList as well:
var regionArray = _utils.GetRegions(someParam).ToList();
now , you need to check regionArray after that :
if(regionArray.Count == 0)
{
// do something
}
Or using Linq
if(!regionArray.Any())
{
// do something
}
if the collection is not empty then you can iterate through the list and validate each string inside this list before you process it:
foreach (var region in regionArray)
{
// check if the element is null or empty
// if true, will skip this element and go to the next one
if(string.IsNullOrEmpty(region)) { continue; } // go to the next iteration
// get the results
var queryResult = myDataSets.FirstOrDefault(x=> x.Location == region);
if (queryResult == null)
{
// do stuff
}
else if (queryResult.State != States.Provisioned)
{
// do stuff
}
}

Linq List.Contains(x => ...) - CS0103 x does not exist in the current context (within foreach)

I've got two simple Lists that I'm trying to synchronise.
List<Guid> untrackedList contains a bunch of Guids.
List<Foo> trackedList contains a number of objects Foo that have (amongst others) an ID property of Guid. This list is tracked by entity framework, so I'd like to add new items for those present in untrackedList that aren't already in trackedList, and remove everything from trackedList that isn't present in untracked List as well.
public void MyTestMethod()
{
const int someThing = 1000;
var untrackedList = new List<Guid>()
{
new Guid("8fcfb512-ca00-4463-b98a-ac890b3ac4da"),
new Guid("6532b60b-f047-4a96-9e5f-c5a242f9a1f5"),
new Guid("103cb7e4-1674-490c-b299-4b20d90e706c"),
new Guid("6c933cce-fb0e-4e1b-bbc3-e62235933cc8")
};
var trackedList = new List<Foo>()
{
new Foo() { SomeId = new Guid("6532b60b-f047-4a96-9e5f-c5a242f9a1f5"), Something = someThing },
new Foo() { SomeId = new Guid("12345678-abcd-1234-1234-1234567890ab"), Something = someThing }
};
// testing Find and Exists
var testFind = trackedList.Find(x => x.SomeId == new Guid("6532b60b-f047-4a96-9e5f-c5a242f9a1f5")); // finds one Foo
var testExists = trackedList.Exists(x => x.SomeId == new Guid("12345678-abcd-1234-1234-1234567890ab")); // == true
foreach (var guid in untrackedList)
{
// add all items not yet in tracked List
if (!trackedList.Exists(x => x.SomeId == guid))
{
trackedList.Add(new Foo() { SomeId = guid, Something = someThing });
}
}
// now remove all from trackedList that are not also in untracked List (should remove 12345678-...)
trackedList.RemoveAll(x=> !untrackedList.Contains(x.SomeId)); // successful, but also shows CS0103 in the debugger
}
This is probably not the most efficient way of doing it, but it appears to work. However, when running this through the debugger, I was thrown off by the error CS0103 "The name 'x' does not exist in the current context"
What causes this error? Why does it not result in an exception?
The same error is shown in the debugger on the .RemoveAll method (last line).
It is simple, usually that error is occured when variable is not scope and can not be calculated, that is excaly what happens if compiler tries to calculate x, x is not really calculated at the scope of method MyTestMethod, there is an expression build from query and caculated in different scope, because x is part of expression it is calculated in different scope.

Type and identifier are both required in a foreach statement using an object

I have the following code that was converted from vb to C#
private void LoadDropDownList()
{
DropDownList location = (DropDownList) Form.FindControl("ddlLocation_ID");
DropDownList vendorID = (DropDownList) Form.FindControl("ddlVendor_ID");
//-- Load the Sales Locations
dtDataList_v10_r1.List objList = new dtDataList_v10_r1.List();
DataSet ds = default(DataSet);
DataView dvActiveLocations = null;
ds = objList.GetSalesLocationDataset(mobjSecurity.SQLDatabase, mobjSecurity.Inst_ID);
if ((ds != null)) {
if (ds.Tables.Count > 0) {
dvActiveLocations = new DataView(ds.Tables[0]); //changed to square brackets per c# syntax 10/9/15 Max //
dvActiveLocations.RowFilter = "status='A'";
}
}
//ddlLocation_ID.DataSource = dvActiveLocations;
//ddlLocation_ID.DataTextField = "ChannelName";
//ddlLocation_ID.DataValueField = "Channel_ID";
//ddlLocation_ID.DataBind();
location.DataSource = dvActiveLocations; // changed to reference control and c# syntax 10/9/15 Max //
location.DataTextField = "ChannelName"; // changed to reference control and c# syntax 10/9/15 Max //
location.DataValueField = "Channel_ID"; // changed to reference control and c# syntax 10/9/15 Max //
location.DataBind(); // changed to reference control and c# syntax 10/9/15 Max //
//-- Load the Available Auction downloads
dtIntegration_v10_r1.Vendor objVendor = default(dtIntegration_v10_r1.Vendor);
dtIntegration_v10_r1.Vendor[] objVendors = null;
dtIntegration_v10_r1.Auctions objAuctions = new dtIntegration_v10_r1.Auctions( ref mobjSecurity); //added ref key word 10/9/15 Max //
objVendors = objAuctions.Vendors;
foreach (objVendor in objVendors)
if (objVendor.HasVendorRelationship == true)
{
//ddlVendor_ID.Items.Insert(0, objVendor.Name);
//ddlVendor_ID.Items(0).Value = objVendor.Vendor_ID;
vendorID.Items.Insert(0, objVendor.Name);
vendorID.Items[0].Value = Convert.ToString(objVendor.Vendor_ID); //changed to reference control and facilitate conversion to string 10/9/15 Max //
}
}
I get the following error when I execute it
Type and identifier are both required in a foreach statement
Now I realize that a proper statement should look like this
foreach (var somevar in object) ....
I tried this syntax and it throws this error
A local variable named 'objVendor' cannot be declared in this scope because it would give a different meaning to 'objVendor', which is already used in a parent or current scope to denote something else
So I am curious how to fix this particular error. I couldn't find it in any of the topics here so I thought I would ask
With foreach, you need to setup the type for the parameter, as so.
foreach(dtIntegration_v10_r1.Vendor objvendor in objVendors)
{
//your code.
}
You already delcared something named objVendor here:
dtIntegration_v10_r1.Vendor objVendor = default(dtIntegration_v10_r1.Vendor);
You need to pick a unique name in the loop.
The compiler error is very straightforward. You already declared a variable named objVendor which is visible inside the for loop:
dtIntegration_v10_r1.Vendor objVendor = default(dtIntegration_v10_r1.Vendor);
Now here:
foreach (var objVendor in objVendors)
if (objVendor.HasVendorRelationship == true)
which objVendor should be referenced in the second line? The one declared at the declaration of the loop or the second one? There is no way for the compiler to guess correctly, thus your code is incorrect. Fix it by giving the loop variable a different name.
foreach (var xyz in objVendors)
Now I realize that a proper statement should look like this
foreach (var somevar in object) ....
Not necessarily. You can insert the actual type name instead of var. Most of the time both will suffice.

c# copied property loses reference when referenced object is removed from list

Example
Have a look at the following code:
private void DeDuplicateOrganisations()
{
var profileOrgs = _organisations.Where(o => o.ExistsInProfile).ToList();
var kvkOrgs = _organisations.Where(o => !o.ExistsInProfile).ToList();
profileOrgs.ForEach(o =>
{
var duplicate = kvkOrgs.FirstOrDefault(k => k.KvK == o.KvK || k.Title == o.Title);
if (duplicate != null)
{
o.CompanyInfoOrganisation = duplicate.CompanyInfoOrganisation;
o.ExistsInBoth = true;
kvkOrgs.Remove(duplicate);
}
});
_organisations = profileOrgs.Concat(kvkOrgs).OrderBy(o => o.Title).ToList();
}
In this example the property CompanyInfoOrganisation (simply a get; set; property) is copied when an organisation is considered a duplicate. This all works as expected, duplicates are nicely deduplicated.
Also this is true inside this message:
_organisations.First(o => o.ExistsInBoth).CompanyInfoOrganisation != null;
Problem
Now I bind the _organisations list to a listbox
lbxCompanies.DataSource = null;
lbxCompanies.DataSource = _organisations;
lbxCompanies.DisplayMember = "Title";
lbxCompanies.SelectedIndex = -1;
and later on get the selected value:
var org = lbxCompanies.SelectedValue as Organisation;
gbxCompanyInfo.Visible = org != null;
if (gbxCompanyInfo.Visible)
if (org.CompanyInfoOrganisation != null)
// NEVER GETS HERE (but gbxComanpyInfo is visible)
If I try to read the CompanyInfoOrganisation property I always get null while I know the property was set.
Question
What is happening here? How come the property reference is destroyed? How can I prevent this from happening?
The reference you're using only has immediate scope and as soon as the query ends it exits scope and your reference disappears. So when you bind later, the reference is exactly right -- null.
profileOrgs.ForEach(o =>
{
// Right here -- var duplicate has scope ONLY within your query.
// As soon as the query is executed it leaves scope and the reference
// pointer will be null
var duplicate = kvkOrgs.FirstOrDefault(k => k.KvK == o.KvK || k.Title == o.Title);
if (duplicate != null)
{
o.CompanyInfoOrganisation = duplicate.CompanyInfoOrganisation;
o.ExistsInBoth = true;
kvkOrgs.Remove(duplicate);
}
});
Because you're using a class, you need to perform a deep MemberwiseClone on it to get a NEW copy of the object:
o.CompanyInfoOrganisation = (YourInfoType)duplicate.CompanyInfoOrganisation.MemberwiseClone();
When you load the data, load the CompanyInfoOrganisation property along with the root entity; that way it will be already loaded into memory. If using LINQ to SQL, you load via DataLoadOptions, and pass this to the context. If using Entity Framework, you use the Include method in the LINQ query.
It might have to do with capturing of variables inside the lambda. Try substituting the .ForEach to a regular foreach().
Or maybe the CompanyInfoOrganisation in duplicate was null to begin with.
The problem was I used string.Join() to show the values, and the first value to join was null (which is really annoying), resulting in an empty string, leaving me thinking the property was null. However it turned out the property was not null, but has a perfectly valid reference to the object needed. Using the debugger with a little more care would have saved me an hour or so...
Sorry!

Categories

Resources