How do I customize List<T>? [duplicate] - c#

This question already has answers here:
Error 7, argument 1: cannot convert from ' ' to ' ' [duplicate]
(3 answers)
Closed 9 years ago.
I am trying to customize List. I have it mostly figured out but am coming across a problem. Here is the code I am working with:
public class MyT
{
public int ID { get; set; }
public MyT Set(string Line)
{
int x = 0;
this.ID = Convert.ToInt32(Line);
return this;
}
}
public class MyList<T> : List<T> where T : MyT, new()
{
internal T Add(T n)
{
Read();
Add(n);
return n;
}
internal MyList<T> Read()
{
Clear();
StreamReader sr = new StreamReader(#"../../Files/" + GetType().Name + ".txt");
while (!sr.EndOfStream)
Add(new T().Set(sr.ReadLine())); //<----Here is my error!
sr.Close();
return this;
}
}
public class Customer : MyT
{
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class Item : MyT
{
public int ID { get; set; }
public string Category { get; set; }
public string Name { get; set; }
public double Price { get; set; }
}
public class MyClass
{
MyList<Customer> Customers = new MyList<Customer>();
MyList<Item> Items = new MyList<Item>();
}
In the code, you can see that I am trying to create a customize List.
Here you also see two of the many classes that I have. All of the classes have an ID.
All of the classes are matched with a custom List.
The problem seems to be in MyList<T>.Read() - Add(new T().Set(sr.ReadLine()));
Lastly, I get that MyT can not be converted to T. I need to know how to fix it.

The Set method returns the type MyT instead of the specific type. Make it generic so that it can return the specific type:
public T Set<T>(string Line) where T : MyT {
int x = 0;
this.ID = Convert.ToInt32(Line);
return (T)this;
}
Usage:
Add(new T().Set<T>(sr.ReadLine()));
Or cast the reference back to the specific type:
Add((T)(new T().Set(sr.ReadLine())));

Related

Generic list of generic lists

I've been playing with various ways of using generics and have hit a road block.
Consider the following classes:
public abstract class DataElement
{
public int Id { get; set; }
}
public class School : DataElement
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
}
public class Course : DataElement
{
public int Id { get; set; }
public int SchoolId { get; set; }
public string Name { get; set; } = string.Empty;
}
public class Student : DataElement
{
public int Id { get; set; }
public int CourseId { get; set; }
public string Name { get; set; } = string.Empty;
public string Phone { get; set; } = string.Empty;
public string Email { get; set; } = string.Empty;
}
Considering a hypothetical scenario where none of this data changes, I'm trying to create a DataDictionary class to house those objects in their respective Lists all within one top-level List property. This crazy idea came to me, when I was writing code to load the different data types from JSON files. I was able to write one load method that could read all three types of data using generics, and that sent me down this particular rabbit hole.
public interface IDataDictionary
{
public List<T> GetAllItemsFromList<T>();
public T GetSingleItemFromList<T>(int id);
}
public class DataDictionary : IDataDictionary
{
public List<IList> Data = new List<IList>();
// Return all objects from the list of type T
public List<T> GetAllItemsFromList<T>()
{
return Data.OfType<T>().ToList(); // This works, returning the appropriate list.
}
// Return specific object from the list of type T by Id property value
public T GetSingleItemFromList<T>(int id)
{
List<T> list = Data.OfType<List<T>>().First(); // This works, resolving to the correct list (e.g. Courses).
return list.Where(i => i.Id == id).First(); // This doesn't work. It doesn't appear to know about the Id property in this context.
}
}
GetAllItemsFromList works fine, returning the appropriate list of items
List<School> newList = GetAllItemsFromList<School>();
However, I am unable to figure out how to return a single item by Id from its respective list.
School newSchool = GetSingleItemFromList<School>(1);
It could be that I'm just trying to be too clever with this, but I can't help but think there is a way to do this, and I'm just missing it.
This doesn't work. It doesn't appear to know about the Id property in this context.
Yes, because you have not specified any constraints to the type parameter T, so it can be anything. There are multiple options:
Create an interface IHaveId and constraint T to it (or use DataElement):
public interface IHaveId
{
int Id { get; set; }
}
public interface IDataDictionary
{
public List<T> GetAllItemsFromList<T>();
public T GetSingleItemFromList<T>(int id) where T : IHaveId; // or where T : DataElement
}
public class DataDictionary : IDataDictionary
{
public T GetSingleItemFromList<T>(int id) where T : IHaveId // or where T : DataElement
{
List<T> list = Data.OfType<List<T>>().First();
return list.Where(i => i.Id == id)
.First();
}
}
add additional parameter to select the id:
public T GetSingleItemFromList<T>(int id, Func<T, int> idSelector)
{
List<T> list = Data.OfType<List<T>>().First();
return list.First(i => idSelector(i) == id);
}
use reflection - I would argue the least recommended option
The problem is that T parameter in function GetSingleItemFromList<T>(int id) could be anything. In order of this to work you should add a constraint:
GetSingleItemFromList<T>(int id) where T: DataElement

How to define a type whose gender can be changed on the fly in code without changing the type of all the properties that are of its type?

I have many entities that use a UserId property of the same type.
Can I define a type (string or int, ...) that I can easily change as a variant for all?
Example:
public class Entity_One
{
public DefineMyType UserId { get; set; }
public string Property_Entity_One { get; set; }
}
public class Entity_Two
{
public DefineMyType UserId { get; set; }
public string Property_Entity_Two { get; set; }
}
const DefineMyType = string;
// or const DefineMyType = int;
// or const DefineMyType = Guid;
Constants can't be used like that.
Preprocessor may be used.
But we can use a generic:
public abstract class AbstractID<T>
{
static protected T Last = default;
public T Value { get; protected set; } // or perhaps init only with C# 9
}
Thus we can define some specialized IDs like:
public class NumberID<T> : AbstractID<T> where T : struct, IComparable, IFormattable
{
public NumberID()
{
Value = (T)( (dynamic)Last + 1 );
Last = Value;
}
}
public class GuidID : AbstractID<Guid>
{
public GuidID()
{
Value = Guid.NewGuid();
Last = Value;
}
}
public class StringID : AbstractID<string>
{
private string Generate()
{
return ...
}
public StringID()
{
Value = Generate();
Last = Value;
}
}
Then we can set the "default" ID type:
public class ManagedID : NumberID<int>
{
}
Or:
public class ManagedID : GuidID
{
}
Therefore we can easily change ManagedID for all code using it.
It only requires to change the ancestor class in the declaration.
And now that works:
public class EntityOne
{
public ManagedID UserId { get; } = new ManagedID();
public string PropertyEntityOne { get; set; }
}
public class EntityTwo
{
public ManagedID UserId { get; } = new ManagedID();
public ManagedID EntityOneId { get; }
public string PropertyEntityTwo { get; set; }
public EntityTwo(EntityOne one)
{
EntityOneId = one.UserId;
}
}
Test
var entity1 = new EntityOne();
var entity2 = new EntityOne();
var entity3 = new EntityTwo(entity1);
Console.WriteLine(entity1.UserId.Value);
Console.WriteLine(entity2.UserId.Value);
Console.WriteLine(entity3.UserId.Value + $" ({entity3.EntityOneId.Value})");
Result with an integer
1
2
3 (1)
Result with a GUID
3a189122-60fd-4dc5-9d7f-3cc4c83375f9
37a9c7de-8ed5-4d02-a1b9-f414db051335
2de962d6-cc91-4e78-b3dc-28acb0ba7f3b (3a189122-60fd-4dc5-9d7f-3cc4c83375f9)
Warning
Here, the use of numbers is very basic and not really reliable, especially beyond a local machine and after stopping the execution of the current process. Thus persistence somewhere of the last value is required for a real database, like in a config file or whatever.
GUID vs INT IDENTITY
Guid vs INT - Which is better as a primary key?
Int for identity/primary key and Guid for public identifier, best practices?

Filling POCO Object with List inside a List [duplicate]

This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed 6 years ago.
I´m attempting to fill a POCO object but I get the NullReferenceException - Object reference not set to an instance of an object, at line "objectAreas.position.Add(objectPositions);" I think I'm not initializing well but I don't see my mistake, let's see the code:
POCO OBJECT
public class GenericQuery
{
public sealed class Areas
{
public int idarea { get; set; }
public string areaname { get; set; }
public List<Positions> positions { get; set; }
}
public sealed class Positions
{
public int idposition { get; set; }
public string positionname { get; set; }
}
public sealed class QueryAreasPositions
{
public int code { get; set; }
public string response { get; set; }
public List<Areas> areas { get; set; }
}
}
Filling It
GenericQuery.QueryAreasPositions objectAreasPositions = new GenericQuery.QueryAreasPositions();
var query = areaRepository.Get(); //Eager Loading EntityFramework List Object, see the AreaRepository at the end
objectAreasPositions.code = 123;
objectAreasPositions.response = "anything";
foreach (var area in query)
{
GenericQuery.Areas objectAreas = new GenericQuery.Areas();
objectAreas.idarea = area.IdArea;
objectAreas.areaname = area.Name;
foreach (var position in area.Position)
{
GenericQuery.Positions objectPositions = new GenericQuery.Positions();
objectPositions.idposition = position.IdPosition;
objectPositions.positionname = position.Name;
***objectAreas.position.Add(objectPositions);***//HERE
}
objectAreasPositions.areas.Add(objectAreas); //And maybe here
}
AreaRepository
public List<Area> Get()
{
using (var context = new Entities())
{
return context.Area.Include("Position").ToList();
}
}
I would appreciate any help/guide you can give me, Thanks.
You are never initializing objectAreas.position, hence the default value for a List<T> is null.
Since you are trying to call the Add method on a null reference, you are getting a NullReferenceException.
To fix this, you should initialize the property before using it:
objectAreas.position = new List<GenericQuery.Positions>();
Alternatively, you can add this logic on GenericQuery.Areas constructor, which would be more appropriate:
public sealed class Areas
{
public int idarea { get; set; }
public string areaname { get; set; }
public List<Positions> positions { get; set; }
public class Areas()
{
positions = new List<Positions>();
}
}
Shouldn't you rather be doing like below. Your position is null cause not yet initialized and thus the said exception.
objectAreas.position = new List<Position>();
objectAreas.position.Add(objectPositions);

Converting inherited class with generic class property

IListModel exposes a generic list property called Items of abstract type ListItemModel. But when I try to convert any derived class to IListModel I get "Object reference not set to an instance of an object" error.
public abstract class ListItemModel
{
public string UserName { get; set; }
public string SomeProperty { get; set; }
}
public interface IListModel<T> where T : ListItemModel
{
List<T> Items { get; set; }
}
public class UserListModel : IListModel<UserListItemModel>
{
public string Query { get; set; }
public int TotalUsers { get; set; }
public List<UserListItemModel> Items { get; set; }
}
public class UserListItemModel : ListItemModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
var users = new UserListModel
{
Query = "a",
TotalUsers = 1111,
Items = new List<UserListItemModel>
{
new UserListItemModel {FirstName = "a", LastName = "b"}
}
};
// later in the application users will be passed around as an object which
// must cast it to IListModel<ListItemModel> in order to access its properties
// but converted will return null
var converted = users as IListModel<ListItemModel>;
foreach (var item in converted .Items)
{
item.SomeProperty = DoSomethingHere(item.UserName);
}
What I am trying to achieve here is being able to populate SomePropery from ListItemModel base class.
Please refer to: as (C# reference)
However, if the conversion isn't possible, as returns null instead of raising an exception.
Basically, what's happening is that you're doing an invalid cast, and the result of that invalid cast depends on the form of casting used.
var converted = users as IListModel<ListItemModel>; // converted is null
var converted = (IListModel<ListItemModel>)users; // raises exception
Is casting actually necessary? UserListModel : IListModel<UserListItemModel> seems to indicate that it IS an IListModel of the type you want, so you should just be able to supply it into the foreach block and work from that, no?
Create a generic list item reference interface:
public interface IListItemModel
{
List<ListItemModel> Items { get; }
}
Have your classes implement with an explicit constructor:
List<LIstItemModel> IListModel.Items
{
get { return this.Items; }
}
And then you can cast users to IListModel.
Thanks to "Brian Mains" and "Kyle Baran" my problem is solved. Here is the working code:
public abstract class ListItemModel
{
public string UserName { get; set; }
}
public interface IListModel
{
List<ListItemModel> ListItems { get; }
}
public abstract class BaseListModel<T> : IListModel where T : ListItemModel
{
public List<T> Items { get; set; }
public List<ListItemModel> ListItems
{
get { return Items.Cast<ListItemModel>().ToList(); }
}
}
public class UserListModel : BaseListModel<UserListItemModel>
{
public string Query { get; set; }
public int TotalUsers { get; set; }
}
public class UserListItemModel : ListItemModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
var users = new UserListModel
{
Query = "a",
TotalUsers = 1111,
Items = new List<UserListItemModel>
{
new UserListItemModel {FirstName = "a", LastName = "b"}
}
};
// and finally I can cast the collection to IListModel
var converted = users as IListModel;
foreach (var item in converted .Items)
{
item.SomeProperty = DoSomethingHere(item.UserName);
}

How to use the same foreach code for 2 collections?

I have 2 collections of 2 different types but have almost the same set of fields.
in one function, I need to iterate through one of the collections depending on one condition.
I want to write only one code block that will cover both cases.
Example:
I have the following code:
if (condition1)
{
foreach(var type1var in Type1Collection)
{
// Do some code here
type1var.Notes = "note";
type1var.Price = 1;
}
}
else
{
foreach(var type2var in Type2Collection)
{
// the same code logic is used here
type2var.Notes = "note";
type2var.Price = 1;
}
}
Now: I want to simplify this code to use the same logic only once ( as they are identical ), something like the following ( P.S : I know the following code is not correct, I am just explaining what I want to do ):
var typecollection = Condition1 ? Type1Collection : Type2Collection;
foreach(var typevar in TypeCollection)
{
// the same code logic is used here
typevar.Notes = "note";
typevar.Price = 1;
}
The definition of Type1 & Type2 is similar to the following code ( Actually they are Entity objects):
public class Type1 : EntityObject
{
public int Type1ID { get; set; }
public int Type1MasterID { get; set; }
public String Notes { get; set; }
public decimal Price { get; set; }
}
public class Type2 : EntityObject
{
public int Type2ID { get; set; }
public int Type2MasterID { get; set; }
public String Notes { get; set; }
public decimal Price { get; set; }
}
Update 1:
I have included some sample code I am using inside foreach block ( I am accessing a public properties of the 2 types).
Update 2:
I have included sample Type1 & Type2 definitions, as you can see I have 2 common Public Properties in both classes which I want to update in foreach block.
Update 3:
I am sorry for the confusion, Type1 & Type2 are derived from EntityObject ( They are both part of my entity model, and the Type1Collection & Type2Collection are actually EntityCollection of these 2 entities.
You could use dynamic. Note you will lose type safety.
var list1 = new List<bool>(){true,false};
var list2 = new List<int>(){1,2};
var typecollection = condition1 ? list1.Cast<dynamic>() : list2.Cast<dynamic>();
foreach (var value in typecollection)
{
//then you can call a method you know they both have
Debug.WriteLine(value.ToString());
}
Or if they share a common interface you can cast directly to that. You will maintain type safety
var list1 = new List<bool>(){true,false};
var list2 = new List<int>(){1,2};
var typecollection = condition1 ? list1.Cast<IConvertible>() : list2.Cast<IConvertible>();
foreach (IConvertible convertible in typecollection)
{
//we now know they have a common interface so we can call a common method
Debug.WriteLine(convertible.ToString());
}
Given Jon Skeet's hint of using LINQ's Concat method and the OP's statement that the classes involved are EntityObjects, here's another possible solution. This assumes that the EntityObject subclasses are defined as partial classes:
public partial class Type1 : EntityObject
{
public int Type1ID { get; set; }
public int Type1MasterID { get; set; }
public String Notes { get; set; }
public decimal Price { get; set; }
}
public partial class Type2 : EntityObject
{
public int Type2ID { get; set; }
public int Type2MasterID { get; set; }
public String Notes { get; set; }
public decimal Price { get; set; }
}
This allows the OP to declare an interface with the common properties, and have his EntityObject subclasses implement that interface:
public interface IMyType
{
String Notes { get; set; }
decimal Price { get; set; }
}
public partial class Type1 : IMyType {}
public partial class Type2 : IMyType {}
And the original code becomes:
var query = (
from type1var in type1Collection
where condition1
select (IMyType)type1var
).Concat(
from type2var in type2Collection
where !condition1
select (IMyType)type2var
);
foreach(var myType in query)
{
myType.Notes = "note";
myType.Price = 1;
}
You could create a base type for type1 and type2 that groups the common properties between the two classes:
class MyBaseType {
// Common properties
}
class Type1 : MyBaseType {
// Specific properties
}
class Type2 : MyBaseType {
// Specific properties
}
Then, you could do something like this:
IEnumerable<MyBaseType> collection;
if(condition1)
collection = type1Collection;
else
collection = type2Collection;
foreach(MyBaseType element in collection) {
// Common logic
}
EDIT:
As Simon points out in the comments, you should use an interface instead of a base type if it's enough (i.e you don't need a specific implementation for both types).
This is not a very nice way to do it, but it would atleast work.
var type1Collection = new Collection<Type1>();
var type2Collection = new Collection<Type2>();
var condition1 = new Random().Next(0, 2) != 0;
dynamic selectedCollection;
if (condition1)
selectedCollection = type1Collection;
else
selectedCollection = type2Collection;
foreach (var typeVar in selectedCollection)
{
typeVar.Notes = "note";
typeVar.Price = 1;
}
I'm surprised nobody else has suggested an extension method yet:
public interface IMyType
{
String Notes { get; set; }
decimal Price { get; set; }
}
public static class MyTypeExtensions
{
public static void MyLogic(this IMyType myType)
{
// whatever other logic is needed
myType.Notes = "notes";
myType.Price = 1;
}
}
Now, your original types just need to implement IMyType:
public class Type1 : IMyType
{
public int Type1ID { get; set; }
public int Type1MasterID { get; set; }
public String Notes { get; set; }
public decimal Price { get; set; }
}
public class Type2 : IMyType
{
public int Type2ID { get; set; }
public int Type2MasterID { get; set; }
public String Notes { get; set; }
public decimal Price { get; set; }
}
Then the original code becomes:
if (condition1)
{
foreach (var type1 in type1Collection)
{
type1.MyLogic();
}
}
else
{
foreach (var type2 in type2Collection)
{
type2.MyLogic();
}
}
You can do it with Predicate and Action stored in a Dictionary. I am suggesting Action here since the code snippet doesn't seems to return anything
public class IterationExample
{
private readonly Dictionary<bool, Action> dictionary;
public IterationExample()
{
dictionary = new Dictionary<bool, Action> { { true, CollectionOneIterator }, { false, CollectionTwoIterator } };
}
public void PublicMethod()
{
dictionary[condition]();
}
private void CollectionOneIterator()
{
foreach (var loopVariable in Type1Collection)
{
//Your code here
}
}
private void CollectionTwoIterator()
{
foreach (var loopVariable in Type2Collection)
{
//Your code here
}
}
}
With this way the readbility and testability of your code improves and also avoids long methods.
Edit:
public class Entity
{
public IList<string> Type1Collection { get; set; }
public IList<string> Type2Collection { get; set; }
}
public class ConsumingClass
{
public void Example()
{
var entity = new Entity();
entity.PublicMethod();
}
}
public static class IterationExample
{
private static readonly Dictionary<bool, Action<Entity>> dictionary;
static IterationExample()
{
dictionary = new Dictionary<bool, Action<Entity>> { { true, CollectionOneIterator }, { false, CollectionTwoIterator } };
}
public static void PublicMethod(this Entity entity)
{
dictionary[condition]();
}
private static void CollectionOneIterator(Entity entity)
{
foreach (var loopVariable in entity.Type1Collection)
{
//Your code here
}
}
private static void CollectionTwoIterator(Entity entity)
{
foreach (var loopVariable in entity.Type2Collection)
{
//Your code here
}
}
}

Categories

Resources