Quick Question: C# Linq "Single" statement vs "for" loop - c#

I need some clarification. Are these two methods the same or different? I get a little bit confused about when the reference to an object passed in a parameter by value is updated and when a new one is created. I know if that assignment creates a new reference, but what about changing a property? Will both of these methods update the field "_someObjectList" the same way?
public class SomeObject{
public Guid UniqueKey { get; set; }
public object SomeProperty{ get; set; }
}
public class SomeObjectListWrapper{
public SomeObjectListWrapper(List<SomeObject> someObjectList){
_someObjectList = someObjectList;
}
private readonly List<SomeObject> _someObjectList;
public void ReplaceItemPropertyValue1(Guid itemUniqueKey, object propertyValue)
{
List<int> resultIndices = new List<int>();
for (var i = 0; i < _someObjectList.Count(); i++)
{
if (_someObjectList[i].UniqueKey == itemUniqueKey)
resultIndices.Add(i);
}
if (resultIndices.Count != 1)
throw new Exception(
"just pretend this is the same exception as Single() throws when it can't find anything");
_someObjectList[resultIndices[0]].SomeProperty = propertyValue;
}
public void ReplaceItemPropertyValue2(Guid itemUniqueKey, object propertyValue)
{
_someObjectList.Single(x=>x.UniqueKey==itemUniqueKey).SomeProperty=propertyValue;
}
}

Because SomeObject is a class (ie. a reference type), both ReplaceItemPropertyValue methods are updating the same object as was inserted into the list and will be retrieved from the list later. (If SomeObject was a struct/value type, the compiler would prevent you from updating an rvalue/return value [1].)
As a minor side-note, your two methods are not actually identical. The Single method raises an exception if there is more than one matching item in the sequence. To properly match the behaviour, use First instead.
"rvalue" is not actually short for "return value," it just happens that in this case your rvalue is a return value, which is why I specified both options.

They may do the same thing depending on the data in your list.
ReplaceItemPropertyValue2 uses the Single method which will throw an exception if itemUnqiueKey is not found or found more than once.
But as long as itemUniqueKey can't appear more than once in the list, the two functions should accomplish the same task.

Both may be same.
The algorithm in the for loop set the object when key matches and then breaks out.
While the LINQ statement will set the object to all entries whose key match. It depends if your collection has same key entered more than once.

Related

How to see if a the first varible of a List with custom class contain somthing?

So if it is a dictionary, I can check if the Key or Value contains something on its own.
But if you have a list with a custom class. Things are different. In the script below, the dictionary is working properly since the wayspotAnchors.ID type is called Guid, and the key of a dictionary is Guid, so in Dictionary, it only compares if it contains the ID by using ContainKey.
However, when I use a List with a custom class. In the Custom class, it has more than one variable which allows my list to store many things.
The problem is when I compare the wayspotAnchors.ID with the List, it compares to everything that is stored in the list which creates an error because the second variable type in the custom list is not matching with the Guid only the first variable is. But there is no way to only compare the first variable of a custom List as Dictionary does.
private void CreateAnchorGameObjects(IWayspotAnchor[] wayspotAnchors)
{
foreach (var wayspotAnchor in wayspotAnchors)
{
if (gameData_List.my_second_game_list.)
{
}
if (_wayspotAnchorGameObjects.ContainsKey(wayspotAnchor.ID))
{
Debug.Log("working");
continue;
}
The custom class
public class MySecondGameList
{
public Guid guid;
public string readable_guid;
public GameObject anchor_gameobject;
}
List of the custom class
public class GameData_List : MonoBehaviour
{
public List<MySecondGameList> my_second_game_list;
}
The error
You need to iterate over the list applying the Any IEnumerable Extension. The Any method requires a lambda expression that returns a boolean value. If, during the enumeration, any lambda expression applied to the enumerated element, returns true, then the Any method stops the enumeration and returns true to your code
var searchedGuid = wayspotAnchor.ID;
if (gameData_List.my_second_game_list.Any(x => x.guid == searchedGuid))
{
// true
}
In stead of Contains you can use the Linq extension Any like this:
if (_wayspotAnchorGameObjects.Any(anchor => anchor.guid == wayspotAnchor.ID))

Any way to avoid boxing/unboxing inside linq

I am trying to write a Linq expression which checks whether any element in the List of objects matches with the predicate.
protected override void ValidateRulesBeforeDelete(List<object> deleteRecords)
{
base.ValidateRulesBeforeDelete(deleteRecords);
var defaultProductStatus = GetDefaultProductStatus();
if (deleteRecords.Any(x => x.Equals(defaultProductStatus.Id)))
{
throw new CustomException(PageResource.ProductStatusViewModel_CannotDeleteDefaultProduct);
}
}
public class ProductStatusViewModel
{
public int Id { get; set; }
}
The above code is working fine. But, recently I read about boxing/unboxing in CLR Via C# and my doubt is, to apply above predicate will the compiler box integer to object for each element in the list??
I am 100% sure all the time the list contains integers boxed as object.
So is there any way I can re-write the above code to avoid boxing/unboxing if there are happening any?.

Code brevity in C# - condensing a setter that throws if null

In prior versions of C#, if you wanted to prevent a null reference exception, you needed to build your setters defensively:
public Guid ItemId { get; set; } //foreign key, required
private Item _item;
public virtual Item Item {
get {
return _item;
}
set {
if(value == null) throw new ArgumentNullException(nameof(value));
_item = value;
ItemId = value.ItemId;
}
}
With more modern implementations, this can be condensed a certain amount using the null-coalescing operator and expression bodies:
private Item _item;
public virtual Item Item {
get => _item;
set => _item = value ?? throw new ArgumentNullException(nameof(value));
}
However, I am curious if this could not be condensed entirely down into a variation of the standard reference:
public virtual Item Item { get; set; }
Such that you do not have to define a private item.
Suggestions? Or is the second code block as efficient/simple as I can get?
I am looking for a solution within the current C# framework, not something I have to spend money on. Right now my use case proposition does not support a paid product
Disclaimer: Those are potential 'alternative' ways of filtering out invalid assignments into properties. This might not provide a straight answer to the question, but rather give ideas how to go on about doing it more generically without defining private properties and defining getters and setters explicitly.
Depending on what Item actually is, you could perhaps create a non-nullable type of Item by creating it as a struct.
Non nullable types are called structs. They are nothing new, they are value types which allow to store properties of type int, string, bool etc.
As on MSDN:
A struct type is a value type that is typically used to encapsulate
small groups of related variables, such as the coordinates of a
rectangle or the characteristics of an item in an inventory.
The following example shows a simple struct declaration:
public struct Book
{
public decimal price{ get; set;}
public string title;
public string author;
}
Reference
Edit (Struct should be sufficient if the object is supposed to be non-nullable type, however if we're talking properties of the class then read below.) :
Another way would be using OnPropertyChanged event which is part of the INotifyPropertyChanged interface.
While the event does not explicitly give you the value that has been changed to, you can grab it as it does provide you the property name. So you could run your validation post assignment and throw then, I suppose however it might not be the best option.
void item_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
var propertyValue = sender.GetType().GetProperty(e.PropertyName).GetValue(sender);
}
Another solution would be using DataAnnotations and add Required attributes on your properties. If I'm not mistaken they will not throw straight away, until you call your own validate function to validate the class, I guess, combined with the above method this would work pretty well and would be pretty generic. Once written you wouldn't have to write your getters and setters explicitly but rather attach just one event to your class and validate it once a property changes.
Here's a small example:
Your Item model for example...
public class Item
{
[Required]
public string Name { get; set; }
}
You would then implement a generic function which would validate all properties.
public bool TryValidate(object #object, out ICollection < ValidationResult > results) {
var context = new ValidationContext(#object, serviceProvider: null, items: null);
results = new List <ValidationResult> ();
return Validator.TryValidateObject(
#object, context, results,
validateAllProperties: true
);
}
Inside that function you would of course throw an exception if validation failed, your results array would contain properties that it failed on an default messages if I'm not mistaken. I believe this is a bit complex, but if you're looking for reducing the number of properties and setter implementations, this could be a step forward. I'm not sure on the overhead etc. Personally, I think on a larger scale, this would be super useful to validate models which are created on the fly from db data or any external source.
Validator Reference | Data Annotations Reference | ValidationResults Reference | PropertyChanged MSDN Sample

Duplicate values in generic list c#

I'm adding values to a c# generic list while trying to prevent duplicates, but without success. Anyone know of a reason why this code below wouldn't work?
I have a simple class here:
public class DrivePairs
{
public int Start { get; set; }
public int End { get; set; }
}
And here is my method which tries to return a generic list of the above class:
ArrayList found = DriveRepository.GetDriveArray(9, 138);
List<DrivePairs> drivePairs = new List<DrivePairs>();
foreach (List<int> item in found)
{
int count = item.Count;
if (count > 1)
{
for (int i = 0; i < (count - 1); i++)
{
DrivePairs drivePair = new DrivePairs();
drivePair.Start = item[i];
drivePair.End = item[i + 1];
if (!drivePairs.Contains(drivePair))
drivePairs.Add(drivePair);
}
}
}
drivePairs = drivePairs.Distinct().ToList();
As you can maybe see, I have an ArrayList, and each row contains a List<int>. What I'm doing is going through each and adding to a list which contains only pairs. E.g. if my List<int> contains [1,3,6,9] I want to add three entries to my pairs list:
[1,3]
[3,6]
[6,9]
It all works fine apart from not recognising duplicates. I thought this line would be enough:
if (!drivePairs.Contains(drivePair))
drivePairs.Add(drivePair);
but it continues to add them all. Even when I add a Distinct() at the end, it still doesn't remove them. I've also tried adding them to a HashSet, but it still includes all the duplicates.
Anyone know of a reason why the duplicates might not be getting picked up?
Your DrivePairs class does not specify equality, as a result, the Contains method will be using reference equality. Add an Equals method that uses both Start and End to determine equality and you will probably find your code works.
See: Equality Comparisons (C# Programming Guide)
List.Contains Method
This method determines equality by using the default equality
comparer, as defined by the object's implementation of the
IEquatable.Equals method for T (the type of values in the list).
Change your DrivePairs class
public class DrivePairs: IEquatable<DrivePairs>
{
public int Start { get; set; }
public int End { get; set; }
public bool Equals(DrivePairs other)
{
return (this.Start == other.Start && this.End == other.End)
}
}
See: http://msdn.microsoft.com/en-us/library/bhkz42b3.aspx
Hope this helps
You are creating new List<int> objects - these are different objects and when compared to each other, even if they contain identical values (in the same or in different orders), will be evaluated as different as the default comparison method on reference types is a reference comparison.
You need to write a custom comparer that will identify equal lists in the manner your application requires.
I've marked Colin's as the answer, but here was the code just in case it's any use to anyone:
Equality comparer:
public class EqualityComparer : IEqualityComparer<DrivePairs>
{
public bool Equals(DrivePairs x, DrivePairs y)
{
return x.StartHub.Equals(y.Start);
}
public int GetHashCode(DrivePairs obj)
{
return obj.Start.GetHashCode();
}
}
and in the controller:
IEqualityComparer<DrivePairs> customComparer = new EqualityComparer();
IEnumerable<DrivePairs> distinctDrivePairs = drivePairs.Distinct(customComparer);
drivePairs = distinctDrivePairs.ToList();
Thanks for all the help and comments
I have not tested it but I think the default equality test is if it is the same instance. Try overriding the Equals method and make it use your properties.
The DrivePairs class type is a reference type(remember reference type and value type concept). So when you check if DrivePairs varible is already added in List collections or not it return false as every DrivePairs varibale has different memory location from other.
Try using either Dictionary or StringDictionary or any other Key value pair collection. It will definately work.

Encapsulation questions in C#

I'm having some problems with encapsulation in C#. There are two specific scenarios that are causing me problems and I believe the issue is related.
Scenario #1
I have a class definition that looks something like this
class MyClass
{
private int _someField;
private OtherClass _otherClass;
public int someField
{
get { return _someField; }
set { _someField = value; }
}
public OtherClass otherClass
{
get { return _otherClass; }
set { _otherClass = value; }
}
}
If I then try and do something like this in a new piece of code
MyClass theClass = new MyClass();
theClass.otherClass.XYZ += 1;
I get told Cannot Modify the return value of 'MyClass.otherClass' because it is not a variable.
Scenario 2#
public partial class trksegType
{
private wptType[] trkptField;
private extensionsType extensionsField;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute("trkpt")]
public wptType[] trkpt
{
get
{
return this.trkptField;
}
set
{
this.trkptField = value;
}
}
}
If I now try and foreach through the wptType array:
foreach (wptType way in trk.trkseg[i])
I get told - foreach statement cannot operate on variables of type 'trksegType' because 'trksegType' does not contain a public definition for 'GetEnumerator'
Even though an array should implicitly allow enumeration.
Can anyone explain what's going on and what I can do to get around this problem, whilst still maintaining best practices.
For scenario 1, I suspect that OtherClass has been defined as a struct. When a struct is accessed from a property accessor a new copy of the struct is created and returned (structs are value types). Changing a property on this new copy will have no effect on the original struct.
The C# compiler detects this and raises that slightly obscure error.
Scenario 1:
The reason is very likely because your OtherClass is a struct and not a class. Value sematics are a bit tricky and mutable value types are considered harmful. So you either want to make OtherClass a class and not a struct or you do something along those lines:
struct OtherClass
{
public int XYZ { get; }
public OtherClass(int xyz)
{
XYZ = xyz;
}
public OtherClass AddToXYZ(int count)
{
return new OtherClass(this.XYZ + count);
}
}
Then you can do
myClass.otherClass = myClass.otherClass.AddToXYZ(1);
Scenario 2:
You either need to implement IEnumerable on trksegType to enumerate over trkpt or actually access trkpt for the enumeration.
In General:
You have violated encapsulation in both scenarios by accessing objects through other objects. Have a look here: http://www.csharp-station.com/Tutorials/lesson19.aspx
You also should consider using better (more explicit) names for your objects. mttng vwls ds nt ncrs rdblty.
(You really shouldn’t post two questions in one.)
Scenario 1
Cannot Modify the return value of 'MyClass.otherClass' because it is not a variable.
This error happens because OtherClass is not a class, but a struct — also called a value type. This means that accessing MyClass.otherClass copies the value instead of returning a reference. You would be modifying this copy, which would be pointless. The compiler catches this because it is always a bug and never useful.
Scenario 2
foreach (wptType way in trk.trkseg[i])
You haven’t told us what trkseg[i] is, but if it is of the type trksegType, then the answer is: because trksegType doesn’t allow any enumeration. It does not implement IEnumerable, IEnumerable<T>, nor does it have a GetEnumerator method of its own.
Perhaps you meant to write:
foreach (wptType way in trk.trkseg[i].trkpt)
because trkpt is an array of wptType. (You might have found this error sooner if you used more meaningful variable names instead of weird combinations of letters that make no sense.)
I can't see anything wrong with your first example - so double check that the sample that errors really does and correct if not.
In the second instance, it looks like you're trying to iterate on an instance of trksegType, rather than the contained trkpt property. Try foreach (wptType way in trk.trkseg[i].trkpt) instead.

Categories