Adding a list item within another list - c#

Ok I have a class similar to the following...
public class Order
{
private Guid id;
[DataMember]
public Guid ID
{
get { return id; }
set { id = value; }
}
private List<Items> orderItems;
[DataMember]
public List<Items> OrderItems
{
get { return orderItems; }
set { orderItems= value; }
}
}
public class Items
{
private string itemName;
[DataMember]
public string ItemName
{
get { return itemName; }
set { itemName = value; }
}
}
When I reference in my code I have a method that takes in an "Order" list as the parameter.
ACME.Order newOrder = new ACME.Order();
ACME.Items newItems = new ACME.Items();
newOrder.ID = xxx
newItems.ItemName = xxx
SendOrderWithItemsFunction(newOrder)
The above works fine however I don't have an add function for my items so that I can do something like the following
newOrder.Items.Add(newItem);
and
newOrder.Items = newItems
will not work because it says that it can not implicitly convert newOrder.Items to newItems[].
What am Missing?

I think I might be missing something, but newOrder.OrderItems.Add(newItem) should work just fine, according to waht you have in your post.
Just some other nitpick things:
The pluralization of the "Items" class is wierd, if it is only a single Item. This is probably the reason that it looked "ok" to assign a single item to a List property.
You may have cut it out of your post, but every class that is being serialized by WCF must be marked as a "DataContract", not just the members of the class.
When initializing objects like this, I think it makes it a lot cleaer to use Type Initializers:
var NewOrder = new ACME.Order{
ID = xxx,
OrderItems = new List<ACME.Item>
{
new ACME.Item{
ItemName = xxx
}
}
};

What you do have is an add function in your Order.OrderItems property:
newOrder.OrderItems.Add(newItem);
you can even add a whole list of items to your OrderItems:
var someList = new List<Items>();
//populate someList here
newOrder.OrderItems.AddRange(someList);

You should be able to do:
newOrder.OrderItems.Add(newItem);
If your newItems[] is an array, you need to do this:
newOrder.OrderItems.AddRange(newItem.ToList<Items>());

You have declared newItems as an ACME.Items type, but the OrderItems property of your Order class is a List<Items>. Those types are not assignable from one to the other directly. So, an assignment of newOrder.OrderItems = newItems is like trying to sayList<Items> = Items. That isn't possible based on the classes you outlined above. Instead, you will need to add to the list.

When you have a list within a list, and the Add() method is missing, a workaround is to make a new list, add the items, then set the inner list to the new list. Instead of:
outerList.innerList.Add(item)
..use..
var newList = new List<ItemType>();
newList.Add(item);
outerList.innerList = newList;

Related

Initialize an IEnumerable<T> to a new List<T>

I have a class like this:
public class ItemList
{
[JsonProperty("items")]
public IEnumerable<Item> Items { get; set; }
}
And I want to initialize empty list, like this:
var newItemList = new ItemList
{
Items = new List<Item>()
};
But the Items remains an IEnumerable and I can't use Add or other methods of a list.
Why not declare it as a list
public class ItemList
{
[JsonProperty("items")]
public List<Item> Items { get; set; }
}
List is IEnumerable by extention, so it would make sense to declare it as a list and have all the methods in place.
Since you already declared is public IEnumerable<Item> Items { get; set; } it will not change.
You can do this:
Items = new List<Item>();
and even
ItemList itemlist = new itemlist();
itemlist.Items = new List<Item>();
((List<Item>)itemlist.Items).Add(new Item());
But ((List<Item>)itemlist.Items).Add(new Item()); is not safe. It will work when you initialize as a list, but it will lead to a runtime exception if you try to cast from some other IEnumerable type.
Having IEnumerable allows you to accomplish one of the SOLID principles that say that you must rely on abstraction rather than on concrete classes. Meaning that you can store whatever implementation of IEnumerable within that property (List, HashSet, etc ...).
You have to convert IEnumerable to List If you want to use the Add method implemented by the List class
var myList = newItemList.ToList<T>();
myList.Add(new T());
where T is your concrete class.
You can use either of the below two options.
You can use List Initializer like below
var itemList = new ItemList() {
Items = new List<Item>() {
new Item() { Name = "One" },
new Item() { Name = "Two" }
}
};
You can initialize the list fully and then assign it to the Items
Much better approach would be to use a Builder pattern which would help in constructing the ItemList as described at https://code-maze.com/fluent-builder-recursive-generics/
Sure you can, just cast it:
(Items as List<Item>).Add(someItem);
It's gonna get pretty boring casting it all the time, perhaps:
public class ItemList
{
private List<Item> _items - new List<Item>;
[JsonProperty("items")]
public IEnumerable<Item> Items { get => _items; private set => _items = value as List<string>; }
}
Then inside the class you can use _items.
I recommend you make that setter private and set the items to be a List internally to the class; the cast is needed on the set, but if someone else passes in an IEnumerable that isn't a List it will cause the items collection to be set to null.
If you're going to make the decision that external classes will see an IEnumerable but you're going to have it be a List, then you shouldn't give anyone using your class the opportunity to change it to something that's not a List..
If you're only ever going to set the list once (never change it for a new instance) e.g. in the constructor, then you can make the property readonly instead

Removing items from a List without losing original reference - logic issue

myModelList is a List<MyModel> object. MyModel consist of the following elements.
MyModel
public bool Name{ get; set; }
public bool Age { get; set; }
public List<School> SchoolsAttended{ get; set; }
So basically, I have a list of Object (myModelList) that contains a List of object (SchoolsAttended).
In myModelList object there are 2 elements of SchoolsAttended and in the removeSchool method I am removing the first element as shown in the code. Upto now, everything works as expected.
However, when the foreach loop goes through its 2nd iteration, the SchoolsAttended object only has 1 element in it. (this is because we removed an element in the previous iteration).
My question is, I need to maintain the same copy of myModelList in the main method, despite removing an object from the removeSchool.
In other words, It should not change the values in the original object which is myModelList.
Main method
foreach(var l in myModelList)
{
removeSchool(l.SchoolsAttended);
}
Remove method
public void removeSchool(List<School> school) {
school.RemoveAt(0);
}
Because List<School> is a reference type instance When you use school.RemoveAt(0); that will remove the first item from school
If you want to keep the original data, you can try to use linq Where seconde override method to make you expect.
public static IEnumerable Where(this IEnumerable<TSource>source, Func<TSource, bool> predicate);
The second parameter is your index in your collection.
foreach(var l in myModelList)
{
l.SchoolsAttended = l.SchoolsAttended.Where((k, idx) => idx > 0).ToList();
}
If you want to keep original myModelList you can create its copy:
var myModelList = /* your data here */
var copy = myModelList.Select(m => new MyModel
{
Name = m.Name,
Age = m.Age,
SchoolsAttended = m.SchoolsAttended.Skip(1).ToList()
}).ToList();
Skip method will remove first School from SchoolsAttended
I'm wondering why both items of myModelList reference the same List<School> object.
I suggest that you ensure that all MyModel instances use different lists.
To that end, you can add an initializer to SchoolsAttended and make the property readonly:
public List<School> SchoolsAttended{ get; private set; } = new List<School>();
When creating your model you can call AddRange to fill the list, e.g.:
model.SchoolsAttended.AddRange(otherModel.SchoolsAttended);

Assigning first row from a generic list to an Object

I have a Generic List<SomeCode> list containing multiple values.
I am trying to pass the first row in the list to an object as:
if(list.count==1)
SomeCode sc = list[0];
Can you please tell me why is this happening?
Here is my Class
public class SomeCode
{
private int _Somecodeid;
private string _Somecodeescription;
private string _Somecode;
public int SomeCodeId
{
get { return _Somecodeid; }
set { _Somecodeid = value; }
}
public string SomeCodeDescription
{
get { return _Somecodeescription; }
set { _Somecodeescription = value; }
}
public string Code
{
get { return _Somecode; }
set { _Somecode = value; }
}
}
I populate a list LIST list.
The method Contains an object
SomeCode sc = new SomeCode
Now when I Assign the very first row to the object as
if(list.count==1)
SomeCode sc = list[0];
Then all the values get assigned except Code.
It takes all the items in the list except the first one. The first item gets printed like this:
"sc.SomeCode= SomeCode"s
however other values are added properly
sc.SomeCodeDescription = "Hello"; etc
When I explicitly assign the values, then it works:
sc.firstItem = list[0].firstItem
Try overloading the "=" operator and assign the matching values.
Something like this
http://msdn.microsoft.com/en-us/library/aa288467(v=vs.71).aspx
If your target Framework is 3.5 or higher I think what you are looking for could be provided using LINQ. Keep in mind you will have to Import System.Linq; Try something like this,
if(list.count==1)
SomeCode sc = list.First();

Property Accessors on List<T> in .NET

I have a property called Fruits which contains a comma delimited string in the form "apples,bananases,peaches"
I want to make a list in the same class which makes the Fruits property easier to manipulate. Accessors won't work as they are not supported on lists or so it seems.
Basically i want a property called FruitList which auto populates based on the Fruits Property and when adding items or manipulating the FruitList it should auto populate the fruits property.
I need the Fruits Property for Entity framework.
You can invert the logic:
List<string> fruitsList = new List<string>();
public List<string> FruitsList
{
get
{
return fruitsList;
}
}
public string Fruits
{
get
{
return string.Join(',', fruitsList);
}
set
{
// Incomplete, does not handle null
FruitsList.Clear();
FruitsList.AddRange(value.Split(','));
}
}
You don't need to worry about updating Fruits if Fruits is determined by looking at FruitsList. You mention that you need Fruits as a string property for Entity Framework, but EF does not care whether it is backed by a string field.
The only realistic way to do this is to use a collection which can be observed for changes, and handle the event raised when it is changed, and update the property.
Something like ObservableCollection<T> would fit the bill.
example:
public class MyObject
{
public string Fruits{get;set;}
public IList<string> FruitList
{
get
{
var list = new ObservableCollection<string>(Fruits.Split(','));
list.CollectionChanged += (s,ea) => {
var items = (IList<string>)s;
Fruits = String.Join(",",items);
};
return list;
}
}
}
Usage:
var obj= new MyObject(){ Fruits="Apple,Banana,Orange" };
var list = obj.FruitList;
list.Add("Satsuma");
list.Add("Grapes");
list.Remove("Apple");
Console.WriteLine(obj.Fruits); // Output: Banana,Orange,Satsuma,Grapes
Live example: http://rextester.com/KCT33825
Having seen the concept here work, it's worth noting that the above implementation is frought with a bit of danger. It creates a new ObservableCollection every time you call the get accessor on it, which could have some unintended consequences.
For example, if you add the following line just before my original Console.WriteLine:
Console.WriteLine("{0}", obj.FruitList == list);
It outputs false which might seem strange as you might (and notionally, should) expect list and obj.FruitList to point to the same list.
You can get round this by changing the implementation to create only ever 1 ObservableCollection and always returning that from the get accessor:
public class MyObject
{
private string fruits;
private ObservableCollection<string> fruitList;
public string Fruits
{
get{ return this.fruits; }
set
{
this.fruits = value;
this.fruitList = CreateFruitList();
}
}
private ObservableCollection<string> CreateFruitList()
{
var list = new ObservableCollection<string>(this.fruits.Split(','));
list.CollectionChanged += (s,ea) => {
var items = (IList<string>)s;
this.fruits = String.Join(",",items);
};
return list;
}
public IList<string> FruitList
{
get
{
return fruitList;
}
}
}
Now all is right with the world again!
here's what you could do, create a proxy for your comma separated list:
public class MyClass
{
public string Fruits {get;set;}
public string [] FruitList {
get { return Fruits.Split(new [] {','}); }
//warning, the setter is dangerous
set { Fruits = string.Join(',', value); }
}
}
When I say the setter is dangerous, I only mean that if you change one element of the array, the Fruit won't be updated. It'll only be updated if you push a new array. If you need that behavior, consider implementing it using an ObservableCollection

Casting class to interface and back

I have the following:
public interface ICartItem
{
string Name { get; set; }
}
public class CartItem : ICartItem
{
public string Name { get; set; }
}
I then create a List and cast it to an interface:
IList<CartItem> items = new List<CartItem>()
{
new CartItem() { Name = "MyItem" }
};
IList<ICartItem> cartItems = items.Cast<ICartItem>().ToList();
Is there a way to cast it back again like illustrated below?
IList<CartItem> newList = cartItems as IList<CartItem>;
Do you need a copy of the list?
If yes, and you are sure that there are only CartItems within the list you can do it with
IList<CartItem> newList = cartItems.Cast<CartItem>().ToList();
But i think you'd like it a little more robust. In that case you can try it with
cartItems.Where(item => item as CartItem != null).Cast<CartItem>().ToList();
But i think you need to create a new list. I can't think of a way to work on the same with an IList interface (IEnumerable<T> works as shown above).
This is not a logical issue because you cannot make sure that all the items in the 'cartItems' list can be casting to 'CartItem' type.
Basically, the answer is no, IList<ICartItem> cannot be cast back to IList since CartItem is not the only type that might implement ICartItem. The cast cannot be type checked by the compiler since it does't know what will be inside the list at runtime.
In C# 4, you can do this:
IEnumerable<CartItem> items = new List<CartItem>
{
new CartItem() { Name = "MyItem" }
};
IEnumerable<ICartItem> cartItems = items;
(i.e. no need for the use of .Cast<ICartItem>().ToList())
Note that the interface is IEnumerable<> and not IList<> since only some interfaces were made covariant in C# 4 (the full list cn be found here: http://msdn.microsoft.com/en-us/library/dd233059.aspx).
However, even in C# 4, adding the following line will cause the compiler to fail:
IEnumerable<CartItem> newList = cartItems;

Categories

Resources