Create a Generic list of <T> - c#

I want to create a method that will create a List of generic objects. Is it possible? Something like this:
public class Mtrl
{
public string Id { get; set; }
public string Name { get; set; }
public string Code { get; set; }
public string Aa { get; set; }
}
public class Trdr
{
public string Id { get; set; }
public string Name { get; set; }
public string Code { get; set; }
public string Address { get; set; }
public string AFM { get; set; }
public string Phone01 { get; set; }
public string Aa { get; set; }
}
And then with a generic class to create my list:
public class GenericClass<T>
{
public List<T> GetData<T>()
{
List<T> myList = new List<T>();
if (typeof(T) == typeof(Trdr))
{
myList.Add(new Trdr());//Error 1: cannot convert from Trdr to 'T'
}
if (typeof(T) == typeof(Mtrl))//Error 2: cannot convert from Mtrl to 'T'
{
myList.Add(new Mtrl());
}
return myList;
}
}
My mistake. I will try to clarify more. The Classes Trdr,Mtrl etc will have many different properties. The Getdata method will take data from a web service via json and i want to create a List of Objects and return it
Something like this:
public List<T> GetData<T>()
{
List<T> myList = new List<T>();
if (typeof(T) == typeof(Trdr))
{
for (int i = 0; i < 100; i++)//fetch data from web api in json format
{
Trdr NewObj = new Trdr();
NewObj.Aa = "...";
NewObj.AFM = "...";
myList.Add(NewObj);
}
}
if (typeof(T) == typeof(Mtrl))
{
for (int i = 0; i < 100; i++)
{
Mtrl NewObj = new Mtrl();
NewObj.Aa = "...";
NewObj.Name = "name ...";
myList.Add(NewObj);
}
}
return myList;
}
}

I suggest extracting an interface (or even a base class):
//TODO: Since you have more than 2 classes, please check the common interface
public interface Idr {
string Id { get; set; }
string Name { get; set; }
string Code { get; set; }
string Aa { get; set; }
}
With all classes of interest implementing it:
public class Mtrl : Idr
{
public string Id { get; set; }
public string Name { get; set; }
public string Code { get; set; }
public string Aa { get; set; }
}
public class Trdr : Idr
{
public string Id { get; set; }
public string Name { get; set; }
public string Code { get; set; }
public string Aa { get; set; }
public string Address { get; set; }
public string AFM { get; set; }
public string Phone01 { get; set; }
}
Now you can use List<Idr> collection; we want the class (T) that implements Idr to have a parameterless constructor as well (new()):
public class GenericClass<T> where T : Idr, new()
{
public List<T> GetData()
{
List<T> myList = new List<T>() {
new T();
};
return myList;
}
}

Sounds like you are trying to create a new list that contains some initial data, and you need to do this for many different types T.
If Mtrl and Trdr has nothing in common and are handled completely differently, consider simply using different methods to get each type of list:
GetDataMtrl(){
var result = new List<Mtrl>();
result.Add(new Mtrl());
return result;
} // etc
Otherwise what you need is Type Constraints of Generic Parameters, that can tell the compiler more about what T is. Or rather what the different values of T must have in common. For example you can do
public List<T> GetData<T>() where T : new()
{
List<T> myList = new List<T>();
myList.Add(new T());
return myList;
}
To say that for T it has to be possible to do new T(). Then you can say GetData<Trdr>() to get a list that contains a single empty Trdr, as you code might seem to try to do.
Maybe you need to set some default values in the data. I notice the classes has a lot of variables in common. Then you might consider using inheritance to specify this commonality:
public class Mtrl
{
public string Id { get; set; }
public string Name { get; set; }
public string Code { get; set; }
public string Aa { get; set; }
}
public class Trdr : Mtrl
{
public string Address { get; set; }
public string AFM { get; set; }
public string Phone01 { get; set; }
}
And then write generic code where T is either of the type Mtrl or its descendant class:
public List<T> GetData<T>() where T : Mtrl
{
List<T> myList = new List<T>();
T MtrlOrTrdr = new T();
MtrlOrTrdr.Id = "my-new-id-";
myList.Add(MtrlOrTrdr);
return myList;
}

Related

How to elegantly map form flat object to complex list object using Automapper?

I have a flat complex type which I need to map to a complex type within a list. I have achieved it using the below code but it is not elegant. The mapping for each individual item has to be specified explicitly even though the types and names match. I wanted to know if there is a more elegant way of doing this without such verbosity and tight coupling?
using AutoMapper;
MapperConfiguration _config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<FlatObject, MyDTO>()
.ForMember(dst => dst.ListObject, opt => opt.MapFrom(src => new List<ListObject> {
new ListObject {
DTOCustObject = new DTOCustObject {
MyString = src.CustType.MyString,
MyInt = src.CustType.MyInt,
MyBool = src.CustType.MyBool,
//Others...
DTOMyObject = new DTOMyObject {
SomeString = src.CustType.MyObject.SomeString
//Others...
}
}
}
}));
});
_config.AssertConfigurationIsValid();
var flatObject = new FlatObject();
flatObject.CustType.MyString = "ABC123";
flatObject.CustType.MyInt = 12345;
flatObject.CustType.MyBool = true;
flatObject.CustType.MyObject.SomeString = "Some String Content";
IMapper mapper = new Mapper(_config);
var myDTO = mapper.Map<MyDTO>(flatObject);
Console.ReadKey();
//###############
//Entity - Source
//###############
public class FlatObject
{
public CustType CustType { get; set; } = new CustType();
}
public class CustType
{
public string? MyString { get; set; }
public int MyInt { get; set; }
public bool MyBool { get; set; }
public MyObject MyObject { get; set; } = new MyObject();
}
public class MyObject
{
public string? SomeString { get; set; }
}
//#################
//DTO - Destination
//#################
public class MyDTO
{
public List<ListObject> ListObject { get; set; } = new List<ListObject>();
}
public class ListObject
{
public DTOCustObject DTOCustObject { get; set; } = new DTOCustObject();
}
public class DTOCustObject
{
public string? MyString { get; set; }
public int MyInt { get; set; }
public bool MyBool { get; set; }
public DTOMyObject DTOMyObject { get; set; } = new DTOMyObject();
}
public class DTOMyObject
{
public string? SomeString { get; set; }
}
public class MyDTO
{
public List<DTOCustObject> DTOCustObjectList{ get; set; } = new List<DTOCustObject>();
}
public class DTOCustObject
{
public string? MyString { get; set; }
public int MyInt { get; set; }
public bool MyBool { get; set; }
public DTOMyObject DTOMyObject { get; set; } = new DTOMyObject();
public DTOCustObject(ICustObjectMapper mapper){
MyString = mapper.GetMyString();
MyInt = mapper.GetMyInt();
MyBool = mapper.GetMyBool();
DTOMyObject = mapper.GetDTOMyObject();
}
}
public class DTOMyObject
{
public string? SomeString { get; set; }
public DTOMyObject(IDTOmyObjectMapper mapper)
{
SomeString = mapper.GetSomeString();
}
}
Now simply implement the Interfaces and their GetMethods on the source object, in the corresponding classes. And presto. No need for any framework of any kind.
This means, no framework, will try change syntax, and force a rewrite. You aren't locked into keeping it updated, there won't be any dependencies on it, etc.
You are free, and the time it takes you to configure, and annotate the code, is equal to the amount of time it takes you to just write the damn interfaces and implementations.
If your objects are autogenerated, simply use the partial keyword, and make the interface implementations in a partial class. Problem solved.
Something to this effect:
public class CustType : ICustObjectMapper
{
public string? MyString { get; set; }
public int MyInt { get; set; }
public bool MyBool { get; set; }
public MyObject MyObject { get; set; } = new MyObject();
string GetMyString(){
return MyString;
}
// etc for the rest of the basic types.
DTOMyObject GetMyObject(){
return new DTOMyObject(MyObject)
}
}
public class MyObject : IDTOmyObjectMapper
{
public string? SomeString { get; set; }
public string GetSomeString()
{
return SomeString;
}
}
Of course, if you don't know how to declare an interface...
public interface ICustObjectMapper {
string GetMyString();
//etc.
}
Now please, for the love of everything on this planet. STOP USING MAPPING FRAMEWORKS IN C#, IT MAKES NO SENSE!

C# get derived class propertiy

I've this base class that contains list of other classes
public class Blacklist
{
public int Id { get; set; }
public virtual IEnumerable<Card> Cards { get; set; }
}
Where Card class looks like
public class Card
{
public int Id { get; set; }
public string Cuid { get; set; }
public int BlacklistId { get; set; }
}
Then I have implemented a derived class that extends Blacklist class
public class BlacklistTwo : Blacklist
{
public new IEnumerable<CardTwo> Cards { get; set; }
}
where CardTwo class extends the base Card class
The problem occurs when I try to invoke a method that accept the base class as parameter with the derived instance. The type of outer class is alright but type of cards stays implemented as base class .
Example:
Insert(
new BlacklistTwo(){
Id = 1,
Cards = new List<CardsTwo>()
{ new CardTwo() { Id = 123123, Cuid = "123213", BlacklistId = 1}});
public void Insert(Blacklist blacklist)
{
blacklist.GetType(); // returns BlacklistTwo
blacklist.Cards.GetType(); // returns IEnumerable<Card> insted of IEnumerable<CardTwo>
}
It works when I set the parameter of method to dynamic but I would like to avoid it if possible.
As pointed out in comments - you don't actually override the property since you use the 'new' keyword. I think this may be what you are trying to achive:
public interface ICard
{
int CardId { get; set; }
string Cuid { get; set; }
int BlacklistId { get; set; }
//.. Other methods and properties
}
public class Card : ICard
{
public int CardId { get; set; }
public string Cuid { get; set; }
public int BlacklistId { get; set; }
}
public class CardTwo : ICard
{
public int CardId { get; set; }
public string Cuid { get; set; }
public int BlacklistId { get; set; }
}
public class Blacklist
{
public int Id { get; set; }
public virtual IEnumerable<ICard> Cards { get; set; }
}
public class BlacklistTwo : Blacklist
{
public override IEnumerable<ICard> Cards { get; set; }
}
And then:
public Test()
{
ICard card1 = new Card();
card1.CardId = 123123;
card1.Cuid = "123213";
card1.BlacklistId = 1;
ICard card2 = new CardTwo();
card2.CardId = 123123;
card2.Cuid = "123213";
card2.BlacklistId = 1;
Insert(new BlacklistTwo()
{
Id = 1,
Cards = new List<ICard>() { card1 ,card2 }
});
if (card1 is Card c1)
{
//Yes - this is a type of Card
}
if (card2 is CardTwo c2)
{
//Yes - this is a type of CardTwo
}
}
You could use an interface or an abstract class, and and probably even avoid extending the blacklist class

How do I create a generic List using abstract class?

I have a Json class "GetAllDevices()". My JSON response consists of an Array/List of objects, where each object has the below common properties.
public class GetAllDevices
{
[JsonProperty("_id")]
public string Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("actions")]
public Action[] Actions { get; set; }
public class Action
{
public string _id { get; set; }
public Action_Def action_def { get; set; }
}
public class Action_Def
{
public string _id { get; set; }
public string name { get; set; }
}
}
I want to create 2 generic lists containing all the above properties based on its "type".
lstfoo1 List contains all the properties(_id, name type and actions) where type="foo1". Similarly, lstfoo2 is a List which contains the above properties where type="foo2".
What I have done so far:
string strJson=getJSON();
Foo1 lstfoo1=new Foo1();
Foo2 lstfoo2=new Foo2();
List<Foo1> foo1list= lstfoo1.GetDeviceData(strJson);
List<Foo2> foo2list = lstfoo2.GetDeviceData(strJson);
public class AllFoo1: GetAllDevices
{
}
public class AllFoo2: GetAllDevices
{
}
public abstract class HomeDevices<T>
{
public string type { get; set; }
public string _id { get; set; }
public List<AllFoo1> lstfoo1{ get; set; }
public List<AllFoo2> lstfoo2{ get; set; }
public abstract List<T> GetDeviceData(string jsonResult);
}
public class Foo1: HomeDevices<AllFoo1>
{
public Foo1()
{
type = "foo1";
}
public override List<AllFoo1> GetDeviceData(string jsonResult)
{
var lst =Newtonsoft.Json.JsonConvert.DeserializeObject<List<AllFoo1>>(jsonResult);
var lst1 = lst.Where(x => x.Type.Equals(type)).ToList();
return lst1;
}
}
public class Foo2: HomeDevices<AllFoo2>
{
public Foo2()
{
type = "foo2";
}
public override List<AllFoo2> GetDeviceData(string jsonResult)
{
var lst = Newtonsoft.Json.JsonConvert.DeserializeObject<List<AllFoo2>>(jsonResult);
var lst1 = lst.Where(x => x.Type.Equals(type)).ToList();
return lst1;
}
}
My question is, is there an easier way to do this using abstract classes? Can I directly convert my "GetAllDevices" class into an abstract class and inherit it and deserialize into it and create a generic list?
This should help, if I understand your problem correctly. Let me know if you have questions or it doesn't work as you need. I put this together really quickly without testing.
The way the Type property is defined could be improved but I left it as you had it.
public class MyApplication
{
public void DoWork()
{
string json = getJSON();
DeviceTypeOne foo1 = new DeviceTypeOne();
DeviceTypeTwo foo2 = new DeviceTypeTwo();
IList<DeviceTypeOne> foo1Results = foo1.GetDeviceData(json); // calls GetDeviceData extension method
IList<DeviceTypeTwo> foo2Results = foo2.GetDeviceData(json); // calls GetDeviceData extension method
}
}
// implemented GetDeviceData as extension method of DeviceBase, instead of the abstract method within DeviceBase,
// it's slightly cleaner than the abstract method
public static class DeviceExtensions
{
public static IList<T> GetDeviceData<T>(this T device, string jsonResult) where T : DeviceBase
{
IEnumerable<T> deviceDataList = JsonConvert.DeserializeObject<IEnumerable<T>>(jsonResult);
IEnumerable<T> resultList = deviceDataList.Where(x => x.Type.Equals(typeof(T).Name));
return resultList.ToList();
}
}
// abstract base class only used to house common properties and control Type assignment
public abstract class DeviceBase : IDeviceData
{
protected DeviceBase(string type)
{
if(string.IsNullOrEmpty(type)) { throw new ArgumentNullException(nameof(type));}
Type = type; // type's value can only be set by classes that inherit and must be set at construction time
}
[JsonProperty("_id")]
public string Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("type")]
public string Type { get; private set;}
[JsonProperty("actions")]
public DeviceAction[] Actions { get; set; }
}
public class DeviceTypeOne : DeviceBase
{
public DeviceTypeOne() : base(nameof(DeviceTypeOne))
{
}
}
public class DeviceTypeTwo : DeviceBase
{
public DeviceTypeTwo() : base(nameof(DeviceTypeTwo))
{
}
}
// implemented GetAllDevices class as IDeviceData interface
public interface IDeviceData
{
string Id { get; set; }
string Name { get; set; }
string Type { get; }
DeviceAction[] Actions { get; set; }
}
// renamed and relocated class Action to DeviceAction
public class DeviceAction
{
public string Id { get; set; }
public DeviceActionDefinition DeviceActionDefinition { get; set; }
}
// renamed and relocated Action_Def to DeviceActionDefinition
public class DeviceActionDefinition
{
public string Id { get; set; }
public string Name { get; set; }
}
It should be simple enough to move the implementation of method GetDeviceData() to the base class.
For this to work, you will need to add a constraint on T so the compiler knows a bit more about the base type. You will also need to implement a constructor to populate the concrete type's type string you use around. This is a necessary measure to ensure the value is always populated as it is used for comparison in the method in question:
public abstract class HomeDevices<T> where T: GetAllDevices
{
public HomeDevices(string concreteType)
{
type = concreteType;
}
public string type { get; set; }
public string _id { get; set; }
public List<AllFoo1> lstfoo1 { get; set; }
public List<AllFoo2> lstfoo2 { get; set; }
//This method is now generic and works for both.
public List<T> GetDeviceData(string jsonResult)
{
var lst = Newtonsoft.Json.JsonConvert.DeserializeObject<List<T>>(jsonResult);
var lst1 = lst.Where(x => x.Type.Equals(type)).ToList();
return lst1;
}
}
I hope that helps.

Abstract class generator

I'm at loss here. I want to refactor a part of the code that uses no abstract classes. I'm familiar with json2csharp. That converts a JSON file to the C# classes so it can be easily deserialized.
Is there a similar site/tool that accepts as input several C# classes and generates basic abstract classes based on those?
This would make the refactoring easier as I don't need to create all the different abstract classes.
Very simple example:
Input:
public class TestClass1
{
public string TestID { get; set; }
public string TestName { get; set; }
public int TestValue1 { get; set; }
public TestClass1()
{
}
}
public class TestClass2
{
public string TestID { get; set; }
public string TestName { get; set; }
public int TestValue2 { get; set; }
public TestClass2()
{
}
}
Output:
public abstract class ATestClass
{
public string TestID { get; set; }
public string TestName { get; set; }
protected ATestClass()
{
}
}
You can get something working pretty quickly if you use the Roslyn code analysis and code generation. Here’s a quick example how that could work. Note that this is somewhat fragile with detecting common properties since its based on the syntax instead of the actual semantics (making string Foo and String Foo incompatible properties). But for code that is actually generated by another code generator, this should work fine since the input should be consistent.
var input = #"
public class TestClass1
{
public string TestID { get; set; }
public string TestName { get; set; }
public string OtherTest { get; set; }
public int TestValue1 { get; set; }
public TestClass1()
{
}
}
public class TestClass2
{
public string TestID { get; set; }
public string TestName { get; set; }
public int OtherTest { get; set; }
public int TestValue2 { get; set; }
public TestClass2()
{
}
}";
// parse input
var tree = CSharpSyntaxTree.ParseText(input);
// find class declarations and look up properties
var classes = tree.GetCompilationUnitRoot().ChildNodes()
.OfType<ClassDeclarationSyntax>()
.Select(cls => (declaration: cls, properties: cls.ChildNodes().OfType<PropertyDeclarationSyntax>().ToDictionary(pd => pd.Identifier.ValueText)))
.ToList();
// find common property names
var propertySets = classes.Select(x => new HashSet<string>(x.properties.Keys));
var commonPropertyNames = propertySets.First();
foreach (var propertySet in propertySets.Skip(1))
{
commonPropertyNames.IntersectWith(propertySet);
}
// verify that the property declarations are equivalent
var commonProperties = commonPropertyNames
.Select(name => (name, prop: classes[0].properties[name]))
.Where(cp =>
{
foreach (var cls in classes)
{
// this is not really a good way since this just syntactically compares the values
if (!cls.properties[cp.name].IsEquivalentTo(cp.prop))
return false;
}
return true;
}).ToList();
// start code generation
var workspace = new AdhocWorkspace();
var syntaxGenerator = SyntaxGenerator.GetGenerator(workspace, LanguageNames.CSharp);
// create base class with common properties
var baseClassDeclaration = syntaxGenerator.ClassDeclaration("BaseClass",
accessibility: Accessibility.Public,
modifiers: DeclarationModifiers.Abstract,
members: commonProperties.Select(cp => cp.prop));
var declarations = new List<SyntaxNode> { baseClassDeclaration };
// adjust input class declarations
commonPropertyNames = new HashSet<string>(commonProperties.Select(cp => cp.name));
foreach (var cls in classes)
{
var propertiesToRemove = cls.properties.Where(prop => commonPropertyNames.Contains(prop.Key)).Select(prop => prop.Value);
var declaration = cls.declaration.RemoveNodes(propertiesToRemove, SyntaxRemoveOptions.KeepNoTrivia);
declarations.Add(syntaxGenerator.AddBaseType(declaration, syntaxGenerator.IdentifierName("BaseClass")));
}
// create output
var compilationUnit = syntaxGenerator.CompilationUnit(declarations);
using (var writer = new StringWriter())
{
compilationUnit.NormalizeWhitespace().WriteTo(writer);
Console.WriteLine(writer.ToString());
}
This would generate the following output:
public abstract class BaseClass
{
public string TestID
{
get;
set;
}
public string TestName
{
get;
set;
}
}
public class TestClass1 : BaseClass
{
public string OtherTest
{
get;
set;
}
public int TestValue1
{
get;
set;
}
public TestClass1()
{
}
}
public class TestClass2 : BaseClass
{
public int OtherTest
{
get;
set;
}
public int TestValue2
{
get;
set;
}
public TestClass2()
{
}
}

Trying to work out these interfaces

I'm trying to create some interfaces. The IReportSection object will have one string and a collection of items, which could be different depending on what we're working with. Do I need to make it generic?
The IReport will have one string and a collection of IReportSection.
Here's how I'm trying to define it now.
public interface IReport
{
string ReportName { get; set; }
ICollection<IReportSection> ReportSections { get; }
}
public interface IReportSection
{
string ReportSectionName { get; set; }
ICollection ReportItems { get; }
}
public abstract class ReportSectionBase : IReportSection
{
public string ReportSectionName { get; set; }
public ICollection ReportItems { get; set; }
}
And my models:
pulic class ProjectSubmissionViewModel
{
public int ProjectSubmissionId { get; set; }
public string SubmissionTitle { get; set; }
}
pulic class AffiliateViewModel
{
public int AffiliateId { get; set; }
public string AffiliateName { get; set; }
}
This is how I'm trying to use it in code:
public class ChapterAffiliates : ReportSectionBase
{
public string ReportSectionName { get { return "Chapter Affiliates"; } }
public ICollection<AffiliateViewModel> ReportItems { get; set; }
}
public class ChapterTitles : ReportSectionBase
{
public string ReportSectionName { get { return "Chapter Titles"; } }
public ICollection<ProjectSubmissionViewModel> ReportItems { get; set; }
}
public class SubmissionListViewModel : IReport
{
public ICollection<ProjectSubmissionViewModel> Submissions { get; set; }
public ICollection<AffiliateViewModel> Affiliates{ get; set; }
public string ReportName { get; set; }
public ICollection<IReportSection> ReportSections
{
get
{
var affiliateSection = new ChapterAffiliates
{
ReportItems = Affiliates
};
var titleSection = new ChapterTitles
{
ReportItems = Submissions.Where(s => s.SubmissionTitle.Contains("SomePhrase")).ToList()
};
var sections = new List<IReportSection> { {subSection}, {titleSection} };
return sections;
}
}
}
I'm not sure how to best define this. I'm pretty sure I've done it before, but it's not coming to me.
Are the type parameters for TRType all the same within a certain report? E.g. will you have report sections with different report types in them?
If all types within a report are the same, the solution is relatively simple:
public interface IReport<T> { ... }
If this is not the case - you'll have to do something different, e.g:
public interface IReportSection
{
string ReportSectionName { get; }
ICollection ReportItems { get; }
}
public abstract class ReportSectionBase<TRType> : IReportSection {
...
}
This allows you to put different underlying types in the ReportSections collection related to the report. You'll have to do some more work to get the exact information that you need out of each report section.

Categories

Resources