Use Class as interface in List<IClass> [duplicate] - c#

This question already has answers here:
C# List<Interface>: why you cannot do `List<IFoo> foo = new List<Bar>();`
(9 answers)
Closed 4 years ago.
I'm having issues trying to use a list of interfaces. (I'm probably terrible at explaining this, I've only been coding for a year now, but here goes.)
I have an interface:
public interface IComboBoxItem
{
string Display { get; set; }
int? IntValue { get; set; }
string StringValue { get; set; }
}
And a class that implements that interface:
public class GenericComboBoxItem : IComboBoxItem
{
public virtual string Display { get; set; }
public virtual int? IntValue { get; set; }
public virtual string StringValue { get; set; }
public GenericComboBoxItem(string stringValue)
{
Display = stringValue;
StringValue = stringValue;
IntValue = null;
}
}
Then I take a list of these in my View Model's constructor:
public class TransactionModalVM
{
public TransactionModalVM(List<IComboBoxItem> categoryList)
{
CategoryList = categoryList;
}
public List<IComboBoxItem> CategoryList { get; set; }
}
Yet when I attempt to pass them in
public class TransactionsOM
{
internal TransactionModalVM GetTransactionModalVM()
{
return new TransactionModalVM(new List<GenericComboBoxItem>() { new GenericComboBoxItem("Not yet Implemented") });
}
}
I get an error that it can't convert from List<GenericComboBoxItem> to List<IComboBoxItem>.
I originally ran into this when I was using a class that inherited from GenericComboBoxItem and thought I just had to use and interface instead of inheritance but then found that both classes failed and figured there but be some trick I'm missing here.
This may possibly be a duplicate of something, but I've spent the morning searching with no luck and thought I'd post a new question.
Much appreciation in advance for any help!

It would be a good idea to research covariance and contravariance in C#.
However in your particular case,using an IReadOnlyList instead of a list in your view model would solve your problem.
public class TransactionModalVM
{
public TransactionModalVM(IReadOnlyList<IComboBoxItem> categoryList)
{
CategoryList = categoryList;
}
public IReadOnlyList<IComboBoxItem> CategoryList { get; set; }
}
A List<GenericComboBoxItem> is convertible to an IReadOnlyList<IComboBoxItem>, but not to a List<IComboBoxItem>

Related

Overriding a base virtual property with a derived type is null when passing to JsonResult

I have 2 base classes which 1 for search criteria and other 1 for search results.
I also have 2 derived classes for User object versions of both of those.
When I put a breakpoint in the controller action I can see all properties are populated as I've hardcoded.
When I call this action directly in the browser, each of my derived object properties is null.
I'm guessing the JSON serialization is not able to tell the difference from the base class to the derived one.
Is there a way to solve this?
public class BaseSearchCriteria
{
public int Page { get; set; }
public int RecordsPerPage { get; set; }
}
public class BaseSearchResults
{
public int TotalResults { get; set; }
public virtual BaseSearchCriteria SearchCriteria { get; set; }
}
public class UserSearchCriteria : BaseSearchCriteria
{
public string Username { get; set; }
}
public class UserSearchResults : BaseSearchResults
{
public new UserSearchCriteria SearchCriteria { get; set; }
}
public JsonResult Search(UserSearchCriteria model)
{
var viewModel = new UserSearchResults
{
SearchCriteria = new UserSearchCriteria
{
Page = 1,
RecordsPerPage = 15
}
};
viewModel.TotalResults = 100;
return Json(viewModel, JsonRequestBehavior.AllowGet);
}
Maybe good way to deal with it is to use generics as Daniel A. White propose.
Sample gist here.

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);

Trouble populating a model with a dictionary item

I have a couple of classes I'm having difficulty populating:
public class ta_Room
{
public string url { get; set; }
public double price { get; set; }
public string room_code { get; set; }
}
public class ta_Hotel2
{
public int hotel_id { get; set; }
public Dictionary<string, ta_Room> room_types { get; set; }
}
In my controller I have:
[HttpGet]
public ta_Hotel2 hotel_inventory() //int api_version, string lang)
{
{
ta_Room room = new ta_Room();
room.price = 23;
room.room_code = "1";
room.url = "http://www.nme.com";
ta_Hotel2 hotel = new ta_Hotel2();
hotel.room_types.Add("Single", room);
However I get a NullReferenceException on the last line above.
In the screenshot below, it shows both the hotel and room object have been created - can anyone please advise what I've done wrong please?
Thank you,
Mark
The error is due to the fact you are not building the instance of room_types inside ta_Hotel2. You should add a constructor as follows or just instantiate it within hotel_inventory():
public class ta_Hotel2
{
public int hotel_id { get; set; }
public Dictionary<string, ta_Room> room_types { get; set; }
public ta_Hotel2()
{
room_types = new Dictionary<string, ta_Room>();
}
}
Also note that, from an encapsulation point of view, I would also make the setter of room_types private after that. And, as a side note, I would also rename your classes and members as suggested here.
You cannot assign a value to hotel.room_types before you initialize. Like the way Efran suggest, use a public constructor in ta_Hotel2 class will solve your issue.

C# accessing subclass method by casting

I have the following abstract class:
abstract class ContactQueue
{
public abstract DateTime period {
get; set; }
public abstract String type { get; set; }
public abstract String toString();
}
Now one of the sub classes of this class is the following:
class GeneralPercentageQueue : ContactQueue
{
public GeneralPercentageQueue(DateTime period)
{
this.period = period;
}
public int phone_answer_total {get; set;}
public int phone_answer_percentage_8025 { get; set; }
public int web_answer_percentage_8030 { get; set; }
public int web_answer_percentage_total { get; set; }
public int mail_answer_percentage { get; set; }
public override DateTime period { get; set; }
public override string type { get; set; }
public override string toString()
{
return period.ToString();
}
}
Now since i have several subclass of the abstract class i have created a list that can contain them all i want to loop through that list and access one of the specefic fields to do this i have attempted the following:
foreach(ContactQueue cq in p.GetGeneralEmailPercentageData(start,end))
{
foreach (ContactQueue contactqueue in finalDataList)
{
if (cq.period == contactqueue.period)
{
(GeneralPercentageQueue)contactqueue.mail_answer_percentage = (GeneralPercentageQueue)cq.mail_answer_percentage;
}
}
}
However im getting an error that there is no such field in the object ContactQueue
So how do i access it?
As others have mentioned you're missing parenthesis which is causing the error.
Instead you can use OfType(T) to filter the collections to only the type you want to compare.
foreach(GeneralPercentageQueue cq in p.GetGeneralEmailPercentageData(start,end)
.OfType<GeneralPercentageQueue>())
{
foreach (GeneralPercentageQueue contactqueue in finalDataList.OfType<GeneralPercentageQueue>())
{
if (cq.period == contactqueue.period)
{
contactqueue.mail_answer_percentage = cq.mail_answer_percentage;
}
}
}
This will prevent exceptions at runtime for mismatched types.
You need to add parentheses:
((GeneralPercentageQueue)contactqueue).mail_answer_percentage = ...;
You need to add paranthesis what is happening is the following:
contactqueue.mail_answer_percentage is calledcast is called on contactqueue.mail_answer_percentage to type GeneralPercentageQueue
Because the property mail_answer_percentage is not a property in type ContactQueue the first call fails, and you get the error that mail_answer_percentage isn't a property in ContactQueue
so your code should look like
((GeneralPercentageQueue)contactqueue).mail_answer_percentage =
((GeneralPercentageQueue)cq).mail_answer_percentage;

Combine Polymorphic Lists into One List and Get Derived Type

I don't know if I'm asking this the right way but here's what I want to do:
Model
public abstract class IncomingOrderBase
{
public string Summary { get; set; }
}
public class IncomingProductOrder : IncomingOrderBase
{
public Product Product { get; set; }
}
public class IncomingServiceOrder : IncomingOrderBase
{
public Service Service { get; set; }
}
public class Sale
{
public ICollection<IncomingProductOrder> ProductOrders { get; set; }
public ICollection<IncomingServiceOrder> ServiceOrders { get; set; }
public List<IncomingOrderBase> GetAllOrders()
{
var orders = this.ProductOrders +++PLUS+++ this.ServicesOrders;
return orders.ToList();
}
}
Usage
foreach(var order in sale.GetAllOrders())
{
Console.WriteLine(order.GetType().Name);
//output = "ProductOrder" or "ServiceOrder" not "IncomingOrderBase"
}
How do I compile the two (or more) lists into one list and then get the original (derived) type from the resulting list? I think I had it working using Concat() but I would really appreciate an expert's example on this.
Yes, Concat is what you want:
public List<IncomingOrderBase> GetAllOrders()
{
return ProductOrders.Concat<IncomingOrderBase>(ServicesOrders).ToList();
}
Note that this will only work on .NET 4, which has generic covariance for IEnumerable<T>. If you're using .NET 3.5, you need
public List<IncomingOrderBase> GetAllOrders()
{
return ProductOrders.Cast<IncomingOrderBase>()
.Concat(ServicesOrders.Cast<IncomingOrderBase>())
.ToList();
}
In addition to the suggested solutions, you could consider this, which is somewhat cleaner code:
var result = new List<IncomingOrderBase>(ProductOrders);
result.AddRange(ServiceOrders);
return result;

Categories

Resources