foreach hopping out without an exception thrown - c#

This 'foreach' code will quit working and it appears that it's due to some error. However, no exception is thrown. Is there any good reason? The code INSIDE the loop ( ie where the comment is ) never get reached. The failure is while it's enumerating.
foreach (DeviceOption<int> d in _deviceOptions.Where(d => d.HasChanges))
{
//Call some DAL method
}
In case this is part of the equation, this is the 'DeviceOption' class code:
public class DeviceOption
{
private object _state;
public object State
{
get { return _state; }
set
{
if (_state == value)
{
return;
}
HasChanges = true;
_state = value;
}
}
public bool UserEditable { get; set; }
public DateTime Timestamp { get; set; }
public int UserId { get; set; }
public bool HasChanges { get; set; }
public bool IsNew { get; set; }
public Guid ID { get; set; }
public string DisplayCategory { get; set; }
public string Name { get; set; }
}
public class DeviceOption<T> : DeviceOption where T : IComparable
{
private T _value;
public T Value
{
get { return _value; }
set
{
if (value.CompareTo(_value) == 0) { return; }
HasChanges = true;
OriginalValue = _value;
_value = value;
}
}
public T OriginalValue { get; set; }
}`
UPDATE: I figured this out.
I figured this out. Turns out that an invalid cast was happening and the code was faulting. What's odd is that the exception wasn't being thrown. I know this now because I wrapped the code in a try/catch. The behaviour is acting as if this code is running on seperate thread. Is that how LINQ works?
The reason for the cast is because _deviceOptions is a List and contains derived types such as DeviceOption or or etc. By adding this to the LINQ, things are fine now: '&& d is DeviceOption'
Here is the updated code and how I fixed the invalid cast:
try
{
foreach( DeviceOption<int> d in _deviceOptions.Where( d => d.HasChanges && d is DeviceOption<int>) )
{
//blah blah blah
}
}
catch(Exception ex)
{
//this is how I detected the exception. Don't ask why I didn't think of this before :(
Console.Write( ex.Message );
}

It appears that there are no device options where HasChanges is True. In that case the Where extension method yields an empty enumerable.

Well, given the information you have provided, I have to assume that this query returns an empty enumerable:
_deviceOptions.Where(d => d.HasChanges)
Have you used the debugger at all to see what exactly is happening? Pull the query out of the loop expression and see what it contains.

Related

C# possible null reference CS8602

This should be simple but I can't get ride of this null pointer warning. What can you do?
private static List<OrderHeader> orderHeaders = new List<OrderHeader>{...};
/*Delete order line item from the provided OrderHeader*/
private void DeleteOrderLine(int orderHeaderIndex, int orderLineIndex)
{
if (orderHeaders != null &&
orderHeaders[orderHeaderIndex] != null &&
orderHeaders[orderHeaderIndex].OrderLineItems != null &&
orderHeaders[orderHeaderIndex].OrderLineItems.Count > orderLineIndex
)
{
orderHeaders[orderHeaderIndex].OrderLineItems.RemoveAt(orderLineIndex);
} else
{
Console.WriteLine("Failed to delete the order line. Please try again");
}
}
Here is second attempt.. still not working.
/*Delete order line item from the provided OrderHeader*/
private void DeleteOrderLine(int orderHeaderIndex, int orderLineIndex)
{
if (orderHeaders is not null &&
orderHeaders[orderHeaderIndex] is not null &&
orderHeaders[orderHeaderIndex].OrderLineItems is not null &&
orderHeaders[orderHeaderIndex].OrderLineItems.Count > orderLineIndex
)
{
orderHeaders[orderHeaderIndex].OrderLineItems.RemoveAt(orderLineIndex);
} else
{
Console.WriteLine("Failed to delete the order line. Please try again");
}
}
Here's the the order Header definition
public class OrderHeader
{
public enum OrderTypes
{
Normal = 0,
Staff,
Mechanical,
Perishable
}
public enum OrderStatusTypes
{
New = 0,
Processing,
Complete
}
[Key]
public string OrderId { get; set; } = string.Empty;
public OrderTypes OrderType { get; set; }
public OrderStatusTypes OrderStatus { get; set; }
public DateTime CreateDate { get; set; } = DateTime.Now;
public string CustomerName { get; set; } = string.Empty;
public List<OrderLine>? OrderLineItems { get; set; }
}
Here is the orderLine definition
public class OrderLine
{
public int LineNumber { get; set; }
public string ProductCode { get; set; } = string.Empty;
public ProductTypes ProductType { get; set; } = 0;
[Column(TypeName = "decimal(18,2)")]
public decimal CostPrice { get; set; }
[Column(TypeName = "decimal(18,2)")]
public decimal SalePrice { get; set; }
public int Quantity { get; set; }
}
It is a common warning when using <Nullable>enable</Nullable>
Since you are checking that orderHeaders[orderHeaderIndex].OrderLineItems is not null then you can use ! operator to indicate that it cannot be null after that check, so try:
private void DeleteOrderLine(int orderHeaderIndex, int orderLineIndex)
{
if (orderHeaders is not null &&
orderHeaders[orderHeaderIndex] is not null &&
orderHeaders[orderHeaderIndex].OrderLineItems is not null &&
orderHeaders[orderHeaderIndex].OrderLineItems!.Count > orderLineIndex
)
{
orderHeaders[orderHeaderIndex].OrderLineItems!.RemoveAt(orderLineIndex);
} else
{
Console.WriteLine("Failed to delete the order line. Please try again");
}
}
I'd make life simpler by slightly changing your API and then making the analysis simpler for the compiler:
/*Delete order line item from the provided OrderHeader*/
private bool DeleteOrderLine(int orderHeaderIndex, int orderLineIndex)
{
if(orderHeaders is null) return false;
var header = orderHeaders[orderHeaderIndex];
if(header is null) return false;
var lineItems = header.OrderLineItems;
if(lineItems is null || lineItems.Count <= orderLineIndex) return false;
lineItems.RemoveAt(orderLineIndex);
return true;
}
Note, it's now up to the caller to check the return value and report success to the user, log an error, etc1, 2.
This simplifies the compiler's analysis because it doesn't have to reason about, for instance, whether it's possible for the OrderLineItems property to be capable of modifying the orderHeaders collection such that the next time it's indexed into a different result will be returned.
Note also that it doesn't have to reason about that at all anyway since I only index into the collection once and the compiler knows for certain that local variables don't change their nullability after they've been checked.
1I.e. It's a bit of a smell that it's currently the job of this one method both to change a collection and to know the right way to interact with the users.
2In fact I'd actually probably prefer to do an additional range check on orderHeaderIndex and be throwing ArgumentXxxExceptions rather than returning a bool but that might be too much of a change to accept at this stage
Open your .csproj file and locate the sections that has
<Nullable>enable</Nullable>
Update for more information - disabling it will tell the compiler that you not allowing nullable types on a project scale and if there are null you will handle them

XML Deserialization and string format

I have an XML like this:
"<ArrayOfClsLog xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">
<ClsLog>
<Subject>sth</Subject>
<Value>123456</Value>
<Comment>val</Comment>
</ClsLog>
</ArrayOfClsLog>"
and with this code I desterilize it:
var tmpSerializer = new XmlSerializer(typeof(ObservableCollection<ClsLog>));
tmpResult = tmpSerializer.Deserialize(tmpReader) as ObservableCollection<ClsLog>;
here is my ClsLog:
public class ClsLog
{
public string Subject { get; set; }
public string Value {get; set;}
public string Comment { get; set; }
}
Everything is fine except:
Big Problem
I want the content of value which can be a number like 123456 converts into 123,456
So I've changed Clslog to :
public class ClsLog
{
public string Subject { get; set; }
public string Value {
get
{
return decimal.Parse(Value.ToString()).ToString("N2", System.Globalization.CultureInfo.InvariantCulture);
}
set
{ }
}
public string Comment { get; set; }
}
But nothing changed, also I've tried to change tmpResult with LINQ:
tmpResult=tmpResult.ToList().ForEach(i => i.Value =
decimal.Parse( i.Value.ToString()).ToString("N2", System.Globalization.CultureInfo.InvariantCulture));
So bad exception happened and turned out I can't change it manually.
Question
How I can fix it? (make value from 123456 to 123.456)
You are declaring this property wrong.
public string Value //why a string why not a decimal?
{
get
{
// you have an infinite recursion here
return decimal.Parse(Value.ToString()).ToString("N2", System.Globalization.CultureInfo.InvariantCulture);
}
set { } // the setter is empty anyway so youcan't set it.
}
Instead try this
private decimal _value; // internal field
public decimal Value
{
get
{
return _value; // return internal field
}
set
{
_value = value / 1000; // set the internal field to the value / 1000
}
}

Data binding to a nested property with possibly null parent

I want to perform data-binding to Room.ToNorth.ImageName while ToNorth may be null.
I'm trying to write my own text adventure and have images next to each of the directions a player can go (ie. open/closed door, pathway). When there isn't a room in a given direction I have the controls bound to show a 'no exit' image, this works great so long the player starting location has an exit in all 4 directions, however when one is null I get the following exception
System.ArgumentNullException: 'Value cannot be null. Parameter name:
component'
This is isn't an issue on a new game but I'm making randomly generated dungeons so it can't be guaranteed on a load game. I realise I could probably fudge it by creating a safe space while the bindings are set then moving the player to where they need to be but is there a proper way to handle this?
The closest answer I've found is this question, but I'm not sure I can apply it here due to how it's bound.
private GameSession _CurrentGame;
private BindingSource _BindToPlayer = new BindingSource();
private void BtnNewGame_Click(object sender, EventArgs e)
{
_CurrentGame = new GameSession();
_BindToPlayer.DataSource = _CurrentGame.CurrentPlayer;
PicBoxNorth.DataBindings.Add("ImageLocation", _BindToPlayer,
"CurrentRoom.ToNorth.ImageName", true, DataSourceUpdateMode.OnPropertyChanged,
"Images\\Wall.png");
}
The ToNorth property just gets the object from an an array of exits, but telling it to return an empty exit if it's null (like suggested in the above link) would be problematic in that the exit wouldn't have any rooms to connect to and thus fail to initialise likely throwing an exception itself along the way. To explain better, I have my rooms set up as follows
public Class Room
{
public int ID { get; set; }
public String Name { get; set; }
public String Description { get; set; }
public String FarDescription { get; set; }
public CoOrds Co_Ords { get; set; }
public Boolean UniqueRoom { get; set; }
public Exit[] ExitArr { get; set; }
public Exit ToNorth => ExitArr[0];
public Exit ToSouth => ExitArr[1];
public Exit ToWest => ExitArr[2];
public Exit ToEast => ExitArr[3];
public Exit ToUp => ExitArr[4];
public Exit ToDown => ExitArr[5];
public Exit ToIn => ExitArr[6];
public Exit ToOut => ExitArr[7];
public Room(int id, String name, String desc, String fardesc,bool unique = false)
{
ID = id;
Name = name;
Description = desc;
FarDescription = fardesc;
Co_Ords = new CoOrds();
ExitArr = new Exit[8];
UniqueRoom = unique;
}
And my exits are set up like so
public class Exit
{
public String Description {get;}
public Room RoomA { get; set; }
public Room RoomB { get; set; }
public Boolean CanExitA { get; set; } = true;
public Boolean CanExitB { get; set; } = true;
public Room NextRoom
{
get
{
if (RoomA.PlayerHere && CanExitA)
{ return RoomB; }
else if (RoomB.PlayerHere && CanExitB)
{ return RoomA; }
else
return null;
}
}
public String ImageName
{
get
{
string imagePath = "Images\\";
if (DoorHere != null)
{
return imagePath + DoorHere.ImageName;
}
else
return imagePath + "Pathway.png";
}
}
public Door DoorHere { get; set; }
public Exit(Room roomA, int Adir, Room roomB, int Bdir, Door door = null)
{
RoomA = roomA;
RoomA.ExitArr[Adir] = this;
RoomB = roomB;
RoomB.ExitArr[Bdir] = this;
DoorHere = door;
}
Door is just an unnamed object with a few bools for IsOpen, IsLocked etc. and shouldn't be needed if you want to test this but exits are created anonymously and add themselves to the Exit array in both connecting rooms, hence creating empty ones would fail.
Any suggestions would be greatly appreciated.
As an option, you can create a property to do null-checking and get/set second level property using that property.
For example in your code, you can create ToNorthImageName property to get/set the value of ToNorth.ImageNamein Room class:
public string ToNorthImageName
{
get { return ToNorth?.ImageName }
//set { if (ToNorth != null) ToNorth.ImageName = value; }
}
The property setter is optional, you can remove it if you just want to read the image name.
Then setup data-binding to that property:
PicBoxNorth.DataBindings.Add("ImageLocation", _BindToPlayer, "ToNorthImageName",
true, DataSourceUpdateMode.OnPropertyChanged);

C# How to initialize an object containing a list<T> with standard values

This question is related to this question. I managed to get one step further, but I am now unable to initialize my whole object with default values in order to prevent it from being null at list level. The goal of this is to hand down the "null" values to my SQL query. Ultimately what I want is one record in my DB that will express: This row has been recorded, but the related values were "null".
I have tried Brian's fiddle and it does not seem to work for me to initialize the whole model with standard values.
Expectation: Upon object initialisation the "null" values should be used and then overwritten in case there is a value coming through JSON deserialisation.
Here is what I have tried. None of this will have the desired effect. I receive this error:
Application_Error: System.ArgumentNullException: Value cannot be null.
Every time I try to access one of the lists in the data model.
namespace Project.MyJob
{
public class JsonModel
{
public JsonModel()
{
Type_X type_x = new Type_X(); // This works fine.
List<Actions> action = new List<Actions>(); // This is never visible
/*in my object either before I initialise JObject or after. So whatever
gets initialised here never makes it to my object. Only Type_X appears
to be working as expected. */
action.Add(new Actions {number = "null", time = "null", station =
"null", unitState = "null"}) // This also does not prevent my
//JsonModel object from being null.
}
public string objectNumber { get; set; }
public string objectFamily { get; set; }
public string objectOrder { get; set; }
public string location { get; set; }
public string place { get; set; }
public string inventionTime { get; set; }
public string lastUpdate { get; set; }
public string condition { get; set; }
public Type_X Type_X { get; set; }
public List<Actions> actions { get; set; }
}
public class Actions
{
public Actions()
{
// None of this seems to play a role at inititialisation.
count = "null";
datetime = "null";
place = "null";
status = "null";
}
// public string count { get; set; } = "null"; should be the same as above
// but also does not do anything.
public string count { get; set; }
public string datetime { get; set; }
public string place { get; set; }
public string status { get; set; }
}
public class Type_X
{
public Type_X
{
partA = "null"; // This works.
}
public string partA { get; set; }
public string PartB { get; set; }
public string partC { get; set; }
public string partD { get; set; }
public string partE { get; set; }
}
}
This is how I now initialize the object based on Brian's answer.
JObject = JsonConvert.DeserializeObject< JsonModel >(json.ToString(), new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore});
When I try to iterate over Actions' content, it (logically) gives me above mentioned null error.
for (int i = 0, len = JObject.actions.Count(); i < len; i++)
My current understanding of constructor initialisations:
If I define values such as count = "null"; they should appear in any new object that is created.
If default values are present I would then also expect that a list that has items with default values (such as count for ex.) would be of Count() 1 and not null. How is that even possible?
This will get you out of your bind:
private List<Actions> _actions = new List<Actions>();
public List<Actions> actions { get => _actions; set => _actions = value ?? _actions; }
This causes trying to set actions to null to set it to the previous value, and it is initially not null so it can never be null.
I'm not absolutely sure I'm reading your question right, so here's the same fragment for partA:
private string _partA = "null";
public string partA { get => _partA; set => _partA = value ?? _partA; }
I have found that in some cases, initializing generic lists with their default constructor on your model increases ease of use. Otherwise you will always want to validate they are not null before applying any logic(even something as simple as checking list length). Especially if the entity is being hydrated outside of user code, i.e. database, webapi, etc...
One option is to break up your initialization into two parts. Part 1 being the basic initialization via default constructor, and part 2 being the rest of your hydration logic:
JObject = new List < YourModel >();
... < deserialization code here >
Alternatively you could do this in your deserialization code, but it would add a bit of complexity. Following this approach will allow you to clean up your code in other areas since each access will not need to be immediately proceeded by a null check.

if property1 is empty, then use property2

public string FirstPersonName
{Get; set; }
public string LocationName
{Get; set; }
public PropertyEvidenceBL()
{
if (FirstPersonName==string.empty)
{
//grab the LocationName in its place. How do I write this.
return LocationName; //does not work
}
query GetPropertyReport contains all person location info.
business class is PropertyEvidenceBL that has all objects/properties
It looks like you have a return statement in the constructor from your posted code. Why?
Try something like this.
Also note you most likely want string.IsNullOrEmpty or string.IsNullOrWhiteSpace as your test. FirstPersonName in your case is likely null and not empty. Two very different things.
public string FirstPersonName { get; set; }
public string LocationName { get; set; }
public string PersonOrLocationName {
get {
return !string.IsNullOrEmpty(FirstPersonName) ? FirstPersonName : LocationName;
}
}
// From your post this looks like the class constructor...
public PropertyEvidenceBL
{
// Do something with the name...
string name = PersonOrLocationName;
}
If you're curious about the ? : syntax, it's shorthand for this:
if(!string.IsNullOrEmpty(FirstPersonName) {
return FirstPersonName
}
else {
return LocationName;
}
What you could do is separate out your logic checking if the FirstPersonName is empty. Obviously with more code in there handle the return from the CheckFirstName method (not sure what you are doing with it).
public PropertyEvidenceBL()
{
CheckFirstName();
}
private string CheckFirstName()
{
if (FirstPersonName == string.empty)
{
return LocationName;
}
else
{
return FirstPersonName
}
}

Categories

Resources