initiated a property with different classes from the constructor [duplicate] - c#

This question already has answers here:
Convert List<DerivedClass> to List<BaseClass>
(13 answers)
Closed 3 years ago.
I have a class that have a property (SearchResults) that need to be List<T>, where T is one of my search classes depend on a condition
public class SearchGeneralResponse : ActionResponse
{
public IList<ISearchRecord> SearchResults { get; set; }
public SearchGeneralResponse(MbsObjectType searchType)
{
if(searchType == MbsObjectType.SourceRepo) SearchResults = new List<SearchRecord>();
if(searchType == MbsObjectType.BuildConfiguration) SearchResults = new List<SearchRecordBuild>();
}
}
I can cast new SearchRecord to ISearchRecord. But when I do it with list
this.SearchResults = new List<SearchRecord>();
I get this error:
Cannot implicitly convert type System.Collections.Generic.List 'SearchRecord' to System.Collections.Generic.List 'ISearchRecord'
Here's my interface:
public interface ISearchRecord
{
}
And one of the derived classes:
public class SearchRecord : ISearchRecord
{
public string Name { get; set; }
public string Summary { get; set; }
}
How can I create a List<T> property that can be initialized to a list of a class depending on a certain condition?

Add a cast to your initializer:
this.SearchResults = new List<SearchRecord>().Cast<ISearchRecord>().ToList();

Generics provide compile-time type safety, but in this case you're trying to tell the compiler that it should just trust you that there won't be run-time problems. Its job is to not trust you :)
Consider what would happen if this assignment were allowed and you did this:
IList<ISearchRecord> results; // results is of type IList<ISearchRecord>
results = new List<SearchRecord>(); // but it holds a value of type List<SearchRecord>
results.Add(new SomeOtherSearchRecord()); // ERROR
Since the property SearchResults is of type IList<ISearchRecord>, any code which uses that property can assign any implementation of ISearchRecord to an element of that list. So it needs to always be ISearchRecord and not a more specific implementing type.
Step back and consider the semantics of what your code needs to do. Should SearchResults support any implementation of ISearchRecord? If it's always going to be assigned from SearchRecord then make it that specific type:
public IList<SearchRecord> SearchResults { get; set; }
Or, if it needs to be a list of ISearchRecord (so it can support other implementations) then you'd have to create the list of that type:
this.SearchResults = new List<ISearchRecord>();
Edit: Also, if your new List<>() is just a contrived example and you're actually getting the list from somewhere else, you still need to create a new list. Fortunately the references within that list can still be to the same objects, but the list itself needs to be the correct compile-time type. You could achieve this with:
this.SearchResults = someOtherList.Cast<ISearchRecord>().ToList();
This would create a new list object, of the correct type, containing the same elements as someOtherList.

Related

Trying to convert an C# anonymous type to a strong type

I'm trying to convert some anonymous type back to its original strong type class.
I have some legacy code (which I cannot touch) which create an anonymous class:
public class Cat : FooId
{
public int Id { get; set; }
public string Name { get; set; }
}
var result = new
{
Id = Mapper.Map<TFooId>(someCat)
};
NOTE: I've tried to make this fake class and interface similar to my code.
This then gives me:
result.GetType().ToString() : <>f__AnonymousType1``1[MyProject.Cat]
From here, I'm not sure how to convert this back to a MyProject.Cat instance?
I've tried (and fails):
(MyProject.Cat)result
(dynamic)result
but both fail. The dynamic doesn't throw an error ... but I can't access any properties in it.
C# is a statically typed language, and those two types are not in any way related to one another. Unless you're able to modify the code which defines those types, the way you'd convert from one to the other would be to create a new instance of the target type and populate it from the source object.
For example:
var resultCat = new Cat { Id = result.Id };
Edit: From comments it looks like it may be possible that the Id property on the result object may be an instance of Cat or some other object? You're going to need to do some debugging to find out what your types are.
But the overall concept doesn't really change. If you have an instance of Cat in your results then you can use that instance. If you don't then in order to create one you'd need to create a new instance and populate it with the data you have. Even if two types are intuitively or semantically similar, they are different types.
It's true what David said with regard to the fact that C# is a statically-typed language and that the new instance should be populated from the source the way he suggested.
However, there are work-arounds (though less performant) for that, such as reflection.
Consider you have a console app where you have defined ObjectExtensions as follows:
public static class ObjectExtensions
{
public static TOut Map<TOut>(this object #in)
where TOut : new()
{
TOut #out = new TOut();
if (#in?.GetType() is Type tin)
{
Type tout = typeof(TOut);
foreach ((PropertyInfo pout, PropertyInfo pin) in tout.GetProperties().Join(tin.GetProperties(), pi => pi.Name, pi => pi.Name, (pout, pin) => (pout, pin)))
{
pout.SetValue(#out, pin.GetValue(#in));
}
}
return #out;
}
}
And Class1 as follows:
public class Class1
{
public string A { get; set; } = "A";
public string B { get; set; } = "B";
public string C { get; set; } = "C";
public override string ToString()
{
return $"{{A={A}, B={B}, C={C}}}";
}
}
You will be able to map your anonymous type back to its original strongly-typed class like this:
Console.WriteLine(new { A = "Anonymous A", B = "Anonymous B", C = "Anonymous C" }.Map<Class1>());
Therefore the bloc above should show the following output:
{A=Anonymous A, B=Anonymous B, C=Anonymous C}
In this case, of course, I have assumed that Class1 (Cat in your example) must have a public parameterless constructor. That may not always be the case. There are more sophisticated scenarios of course that might involve other techniques for creating the object such as cloning or dependency injection. Just saying that the idea of yours is possible.

Restrict access to property from outside the class [duplicate]

This question already has answers here:
IEnumerable vs IReadonlyCollection vs ReadonlyCollection for exposing a list member
(5 answers)
Closed 3 years ago.
Let's have this class:
ExampleClass
{
public List<string> MyValue { get; set; }
}
The question is how to restrict outside classes to modify of that property, means add object to collection, make new().
you can have something like this
public ReadOnlyCollection<string> MyValue {get; private set;}
You could expose it as IEnumerable<string> instead of as a list. This interface will not allow adds. You can still store it as a list internally, as a private field, so that the class itself can add or remove if needed.
For example:
class ExampleClass
{
private List<string> _myValue = new List<string>();
public IEnumerable<string> MyValue
{
get
{
foreach (var s in _myValue) yield return s;
}
}
}
If the caller would like to work with its own list, it can of course do this:
var list = exampleClass.MyValue.ToList();
At which point the caller owns it and it is clear that anything it chooses to add has nothing to do with the original list.

How can I iterate over an List<T> to access values when T is returned from a Factory and unknown

Please forgive me if the title is not worded correctly.
I am retrieving data from database tables for various devices and building a list. Different devices could have the same properties and will definitely have some properties that differ, So I am using a Factory Pattern to create whichever is needed at run time.
Factory class:
public interface IImportModel
{
IList CreateImportList(SqlDataReader reader);
}
And Concrete class:
public class Device1ImportModel : IImportModel
{
public string SerialNumber { get; set; }
public string PartA { get; set; }
public IList CreateImportList(SqlDataReader reader)
{
Device1ImportModel linkedItem = new Device1ImportModel();
List<Device1ImportModel> importList = new List<Device1ImportModel>();
while (reader.Read())
{
linkedItem = new Device1ImportModel();
linkedItem.SerialNumber = reader["SerialNo"].ToString();
linkedItem.PartA = reader["PartA"].ToString();
importList.Add(linkedItem);
}
return importList;
}
}
I create the device from the factory:
importModel = ImportModelFactory.CreateImportModel("Device1");
Now when I want to iterate over the importModel, I receive a compile time error on the line where I attempt to access item.SerialNumber
foreach (var item in importList)
{
string number = item.SerialNumber;
}
The error:
'object' does not contain a definition for 'SerialNumber' and no extension method 'SerialNumber' accepting a first argument of type 'object' could be found.
If I place a breakpoint and hover over item variable, I can see the properties and value.
I tried using dynamic instead of var, but then later in my code I can no longer use Linq or Lambda queries.
How can I access the values?
Can I convert the IList to List perhaps using Reflection?
Edit 1
Added Code for CreateImportModel:
static public IImportModel CreateImportModel(DeviceType device)
{
switch (device)
{
case DeviceType.Device1:
return new Device1ImportModel();
case DeviceType.Device2:
return new DeviceImportModel();
default:
return null;
}
}
If you cannot change your method's signature, you can use:
foreach (var item in importList.Cast<Device1ImportModel>())
{
string number = item.SerialNumber;
}
This will throw an exception, however, if there will be an object in the importList collection that is not a Device1ImportModel or its derived class.
If you're not sure that all objects in the list are of that type and want to avoid exceptions, use this apporach:
foreach (var item in importList.OfType<Device1ImportModel>())
{
string number = item.SerialNumber;
}
Change IList to List<Device1ImportModel> (or IList<Device1ImportModel> or IReadOnlyList<Device1ImportModel>).
public List<Device1ImportModel> CreateImportList(SqlDataReader reader)
IList is an older interface (pre-generics) and thus if you use IList (rather than IList<Device1ImportModel) then the compiler / runtime has no notion of the Type of your data (i.e. it treats it as object), thus:
'object' does not contain a definition for 'SerialNumber' and no
extension method 'SerialNumber' accepting a first argument of type
'object' could be found.
You may also need to change the interface to:
public interface IImportModel<T>
{
List<T> CreateImportList(SqlDataReader reader);
}
and the class to:
public class Device1ImportModel : IImportModel<Device1ImportModel>
{
public string SerialNumber { get; set; }
public string PartA { get; set; }
public List<Device1ImportModel> CreateImportList(SqlDataReader reader)
{
You likely also want to change CreateImportModel so instead of calling it like:
ImportModelFactory.CreateImportModel("Device1");
you instead call it like:
ImportModelFactory.CreateImportModel<Device1ImportModel>();
so that a concrete Device1ImportModel is returned (and thus SerialNumber is accessible).
I ended up just using a separate class to hold all the properties, whether each other concrete class uses them or not.
Its not ideal but works.
I would have preferred relying on each concrete class to be responsible for it's own properties though.

The model is of type List, but this dictionary requires IEnumerable [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
In my controller I have a simple LINQ query:
var results = .....ToList();
Then, I want to pass 'results' into a View that renders a grid. It takes a model of:
#model IEnumerable<XXXX.WebSite.Areas.BrokerDashboard.Models.AccountHBSearchItem>
I didn't think this would be a problem, but I get:
The model item passed into the dictionary is of type 'System.Collections.Generic.List, but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable
This confuses me, because from all that I'm reading, a List<T> already is an IEnumerable<T>.
Based on an answer here, I tried this:
IEnumerable<string> eResults = results.ToList();
But that gets me the Can implicitly convert error.
If I don't do the .ToList() then results is an IOrderedEnumerable, and I get this:
The model item passed into the dictionary is of type 'System.Linq.OrderedEnumerable, but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable
EDIT:
This is the entire query:
var results = Context.GetLeadSalesAccountTransactions(accountID)
.OrderBy(a => a.TransactionDate)
.Select(a =>
{
currentTotal -= a.Debit != null ? (decimal)a.Debit : 0;
currentTotal += a.Credit != null ? (decimal)a.Credit : 0;
return new AccountHBSearchItem
{
AccountTransactionID = a.AccountTransactionID,
LeadID = a.LeadSales != null ? a.LeadSales.Lead.LeadID : 0,
Address = a.LeadSales != null ? a.LeadSales.Lead.Address : string.Empty,
LotNumber = a.LeadSales != null ? a.LeadSales.Lead.LotNumber : string.Empty,
Type = a.AccountTransactionType.GetDisplayName(),
Debit = a.Debit,
Credit = a.Credit,
RemainingBalance = currentTotal,
TransDate = a.TransactionDate
};
}
).OrderByDescending(i => i.TransDate).ToList();
Seems like you have different type of generic parameters of your List<T1> and IEnumerable<T2>. From your seccond attempt I see that you are using IEnumerable<string> but model is IEnumerable<MyNamespace.MyModel>. Make sure generic parameters are same. You can pass
List<MyNamespace.MyModel>
IEnumerable<MyNamespace.MyModel>
IOrderedEnumerable<MyNamespace.MyModel>
All of them are appropriate
The return type of the .ToList will be of type<T>.
This means that <T> can be anything that implements a particular interface or is a particular concrete/abstract class. This depends on the class that is using the Generics and returning the List of those <T> objects.
Using an interface constraint on <T> means that you can accept a <T> (or List<T>) without knowing what type of object <T> actually is, but still interact with specific properties or methods according to the interface that <T> must implement.
The #model that will be used in the Razor View is specified as an IEnumerable (a list that you can iterate through) of type xxx.AccountHBSearchItem.
You can then access the properties and methods that are available in AccountHBSearchItem from #model.
For example: #model.AccountTransactionID, #model.LeadId, etc.
But the List<T> isn't a List<xxx.AccountHBSearchItem> - it could be anything. So Razor will not be able to access the properties and methods that you will use on the View for output because it cannot guarantee that the <T> object has an interface that fits. You could be passing a List<Animal> to the View, and any access to properties that xxx.AccountHBSearchItem has will fail (I'd hope!) because an Animal isn't a xxx.AccountHBSearchItem.
So, to solve this problem, you could state in the class used to generate the List<T> that <T> implements the interface that you need to use when generating the View.
Suppose the interface needs the following properties (guessed from your data); create an interface with these properties, to be used on all of the classes that will be used as a <T>.
public interface IAccountHBSearchItem
{
long AccountTransactionID { get; set; }
long LeadID { get; set; }
string Address { get; set; }
string LotNumber { get; set; }
string Type { get; set; }
bool Debit { get; set; }
bool Credit { get; set; }
decimal RemainingBalance { get; set; }
DateTime TransDate { get; set; }
}
Now specify that the different classes implement this interface:
public class AccountSearchAAA<T> where T : IAccountHBSearchItem
{
// Rest of implementation
public List<T> GetAll()
{
// return list of AccountSearchAAA objects here
}
}
public class AccountSearchBBB<T> where T : IAccountHBSearchItem
{
// Rest of implementation
public List<T> GetAll()
{
// return list of AccountSearchBBB objects here
}
}
Then you can change #model to use IEnumerable<xxx.IAccountHBSearchItem> (the interface).
Now you can generate search results of different account types for instance, as long as they implement this interface, and then use them all on the same View.
Accessing the properties and methods available via the interface will now be fine.

assign linq result to an object variable and then get value from that object Variable

I have a situation that needs to assign a LINQ result to a Button.Tag property.
and when that button clicks, iterate throughout that LINQ result placed in the Button.Tag
HINT : LINQ result is type of List<anonymousType>. for some reason, i don't what to return List<KnownType>
any idea?
EDIT : As you all suggested, i reconsider problem and decided to create a specific class type and put DataTableRowId in the class instead of whole DataTableRow thing.
therefore anonymous Type Like new {Class1=c1, Class2=c2, DataTableRow3=dr3} changed to
class of type:
public class CustomClass
{
public Class1 c1 { get; set; }
public Class c2 { get; set; }
public int DataTableRow3Id dr3 { get; set; }
}
You can not access anonymous types this way, You can make a custom class and create the result of linq to that custom type. Assign this object to tag and later type cast it back to your custom type.

Categories