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.
Related
new coder with C# as first language. Due to the type of data I'm processing I'm driven to make generalized classes and functions to avoid retyping the same code for different data types. Take for example this code:
public interface ITDXInput <TSCContext, TContext, TWaferContainer> : ITDXInput
{
TSCContext SCContext { get; set; }
TContext ToolContext { get; set; }
TWaferContainer WaferContainer { get; set; }
}
This base interface is inherited by:
public class CIMTDXInput : ITDXInput<CIMSCContext, CIMToolContext, CIMWaferContainer>
{
public CIMWaferContainer WaferContainer { get; set; }
public CIMSCContext SCContext { get; set; }
public CIMToolContext ToolContext { get; set; }
}
And it's members inherit:
public class CIMSCContext : ISCContext
{
public string PROCESSING_END_TIME { get; set; }
public string PRODUCT_NAME { get; set; }
}
public class CIMToolContext : IToolContext
{
public string LOT { get; set; }
public string TDX_MULTI_FILES { get; set; }
}
Now the third member is a little special as it contains other classes so I tried to implement it like so base interface:
public interface IWaferContainer<TDieData>
{
List<TDieData> DieDataList { get; }
}
When this failed to convert at run time during the delegate handle section which I will get to shortly, I redid the base interface and made it as identical to its child class in case that was the issue:
public interface IWaferContainer
{
CIMWaferContext WaferContext { get; set; }
List<CIMWaferDieMeasurements> WaferMeasurementList { get; set; }
List<CimDieData> DieDataList { get; }
}
But this also gave me the same cannot convert object error. The Class that inherits this interface is this:
public class CIMWaferContainer : IWaferContainer
{
public CIMWaferContext WaferContext { get; set; } = new CIMWaferContext();
public List<CIMWaferDieMeasurements> WaferMeasurementList { get; set; } = new List<CIMWaferDieMeasurements>();
public List<CIMDieData> DieDataList { get; set; }
}
The error occurs at this part of the code:
public class KlarfTemplateDelegateHandler
{
public static Dictionary<string, Delegate> HandlerMap = new Dictionary<string, Delegate>();
public static Dictionary<string, Delegate> DefectListHandlerMap = new Dictionary<string, Delegate>();
delegate string TemplateDelegate<T,U,V>(ITDXInput<T, U, V> cimInput);
delegate string DefectListTemplateDelegate(CIMTDXInput cimInput, List<CIMKlarfDefectList> list);
static KlarfTemplateDelegateHandler()
{
HandlerMap["#DATA.TOOL_CONTEXT.PROCESSING_END_TIME_DATE#"] = new TemplateDelegate<ISCContext,IToolContext, IWaferContainer>(ProcessEndDate);
}
private static string ProcessEndDate<T,U,V>(ITDXInput<T,U,V> cimInput) where T: ISCContext where U: IToolContext where V: IWaferContainer
{
DateTime dateTime = DateTime.Parse(cimInput.ToolContext.PROCESSING_END_TIME);
return dateTime.ToString("MM-dd-yyyy");
}
}
This class is called like so:
public someClass{
protected override string ReplaceHashTag(string input)
{
string result = input;
if (KlarfTemplateDelegateHandler.HandlerMap.ContainsKey(input))
{
result = (string)KlarfTemplateDelegateHandler.HandlerMap[input].DynamicInvoke(CimInput);
}
return result;
}
}
Whenever it tries to run the delegate I get the error:
Object of type 'TDXXMLParser.Entity.CIM.CIMTDXInput' cannot be converted to type 'TDXXMLParser.Entity.ITDXInput`3[TDXXMLParser.Entity.ISCContext,TDXXMLParser.Entity.IToolContext,TDXXMLParser.Entity.IWaferContainer]'
This error occurs for either implementation of IWaferContainer. I'm at my wits end with my lack of experience as a new coder.
How can I fix this? Or do I need to just abandon my design and just copy and paste the code for each type of ITDXInput?
I don't know why it can't convert it when CIMTDXInput is a type of ITDXInput.
I know this is a bit verbose but this was my best attempt at cutting down the code to show what the issue is.
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;
}
I have following code below. I have two main interfaces IWatch and IWatchService. Oryginally Watch() was in IWatchService and there was no IWatch but since that CollectionService cannot use Watch() method i decided (ISP) to create IWatch interface additionally.In CollectionService i want in ctor pass either DatabaseWatchService or RemoteFilesWatchService therefore i put parameter type in ctor as IWatchService<IEntity> watchService nevertheless when in DoIt() method initialize fileWatcherServiceCsv variable it says:
Cannot implicitly convert type 'RemoteFilesWatchService' to
'IWatchService'. An explicit conversion exists (are you
missing a cast?)
public interface IWatch
{
void Watch();
}
public interface IWatchService<TDataEntity> where TDataEntity : IEntity
{
INotificationFactory NotificationFactory { get; }
ObservableCollection<TDataEntity> MatchingEntries { get; set; }
}
public interface IDatabaseWatchService<TDataEntity> : IWatchService<TDataEntity> where TDataEntity : IDatabaseEntity
{
IDatabaseRepository<IDbManager> DatabaseRepository { get; }
}
public interface IRemoteFilesWatchService<TDataEntity> : IWatchService<TDataEntity> where TDataEntity : IFileEntity
{
List<string> ExistingRemoteFiles { get; set; }
List<RemoteLocation> RemoteLocations { get; set; }
IWinScpOperations RemoteManager { get; set; }
IRemoteFilesRepository<IDbManager, TDataEntity> RemoteFilesRepository { get; }
}
public class RemoteFilesWatchService : IRemoteFilesWatchService<IFileEntity>, IWatch
{
public INotificationFactory NotificationFactory { get; }
public ObservableCollection<IFileEntity> MatchingEntries { get; set; }
public List<string> ExistingRemoteFiles { get; set; }
public List<RemoteLocation> RemoteLocations { get; set; }
public IWinScpOperations RemoteManager { get; set; }
public IRemoteFilesRepository<IDbManager, IFileEntity> RemoteFilesRepository { get; }
public RemoteFilesWatchService(IWinScpOperations remoteOperator,
IRemoteFilesRepository<IDbManager, IFileEntity> remoteFilesRepository,
INotificationFactory notificationFactory)
{
RemoteManager = remoteOperator;
RemoteFilesRepository = remoteFilesRepository; //csv, xml or other repo could be injected
NotificationFactory = notificationFactory;
}
public void Watch()
{
}
}
public class DatabaseWatchService : IDatabaseWatchService<DatabaseQuery>, IWatch
{
public INotificationFactory NotificationFactory { get; }
public ObservableCollection<DatabaseQuery> MatchingEntries { get; set; }
public IDatabaseRepository<IDbManager> DatabaseRepository { get; }
public DatabaseWatchService(IDatabaseRepository<IDbManager> databaseRepository,
INotificationFactory notificationFactory)
{
DatabaseRepository = databaseRepository;
NotificationFactory = notificationFactory;
}
public void Watch()
{
}
}
public class CollectionService
{
private IWatchService<IEntity> _watchService;
public CollectionService(IWatchService<IEntity> watchService)
{
_watchService = watchService;
}
}
class Run
{
void DoIt()
{
IWatchService<IEntity> fileWatcherServiceCsv = new RemoteFilesWatchService(new WinScpOperations(),
new RemoteCsvFilesRepository(new DbManager(ConnectionDbType.MySql)),
new NotificationFactory());
var coll1 = new CollectionService(fileWatcherServiceCsv);
}
}
public interface IEntity
{
}
public interface IFileEntity : IEntity
{
int Id { get; set; }
string Name { get; set; }
bool IsActive { get; set; }
bool RemoveFromSource { get; set; }
string DestinationFolder { get; set; }
RemoteLocation RemoteLocation { get; set; }
}
public interface IDatabaseEntity : IEntity
{
}
public class CsvFile : IFileEntity
{
public int ColumnHeader { get; set; }
public int ColumnsCount { get; set; }
public string Separator { get; set; }
public int ValuesRowStartposition { get; set; }
public int ColumnRowPosition { get; set; }
public int Id { get; set; }
public string Name { get; set; }
public bool IsActive { get; set; }
public bool RemoveFromSource { get; set; }
public string DestinationFolder { get; set; }
public RemoteLocation RemoteLocation { get; set; }
}
public class XmlFile : IFileEntity
{
public int Id { get; set; }
public string Name { get; set; }
public bool IsActive { get; set; }
public bool RemoveFromSource { get; set; }
public string DestinationFolder { get; set; }
public RemoteLocation RemoteLocation { get; set; }
public string SubNode { get; set; }
public string MainNode { get; set; }
}
This question gets posted almost every day. One more time!
A box of apples is not a box of fruit. Why not?
You can put a banana into a box of fruit, but you cannot put a banana into a box of apples, so a box of apples is not a box of fruit, because the operations you can perform on them are different. Similarly, a box of fruit is not a box of apples.
You're trying to use a IWatchService (box) of IFileEntity (apples) as an IWatchService of IEntity (fruit), and that's not legal.
Now, you might notice that in C# you can use an IEnumerable<Apple> where an IEnumerable<Fruit> is expected. That works just fine because there is no way to put a banana into an IEnumerable<Fruit>. In every member of IEnumerable<T> and IEnumerator<T>, the T comes out, not in.
If you are in that situation then you can mark your interface as
interface IWatchService<out T> ...
And the compiler will verify that every T in the interface is used in "out" positions, and then will allow the conversion you want.
That conversion is called a generic covariant conversion and it only works when:
The generic type is an interface or delegate
The type parameter is marked out, and the compiler verifies that is safe
The varying types (Fruit and Apple, say) are both reference types. You can't do covariant conversions involving int and object, for example.
Your RemoteFilesWatchService implements interface IWatchService<IFileEntity>, while your CollectionService expects a IWatchService<IEntity>. The two types are different, that's why it cannot convert.
Modify your CollectionService to accept IWatchService<IFileEntity> instead, or make RemoteFilesWatchService implement IRemoteFilesWatchService<IEntity>. Or use a non-generic interface in CollectionService instead.
You cannot have a IWatchService<IFileEntity> and treat it as a IWatchService<IEntity>. Compare it to a List<T> for example. You cannot expect to be able to do this:
class Animal {}
class Bird : Animal {}
class Elephant : Animal {}
var birds = new List<Bird>();
// compiler does not allow this...
List<Animal> animals = birds;
// ...because there is no point in adding elephants to a list of birds.
animals.Add(new Elephant());
Making a slight change to take support from variance, should fix your issue as follows:
public interface IEntity
{
}
public interface IFileEntity : IEntity
{
...
}
public interface IWatchService<out TDataEntity> where TDataEntity : IEntity //note the "out" keyword here.
{
}
You can learn more about Variance in Generic Interfaces Here
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.
How can I have a property which can accept object of any type (of class)... something like this?
public class HttpConnectorRequest
{
public int Id { get; set; }
public T RequestObject { get; set; } where T: class
public string ResponseData { get; set; }
public Exception Exception { get; set; }
}
I am trying to acheive an alternative for something like this:
public class HttpConnectorRequest
{
public int Id { get; set; }
public ClassA ClassARequestObject { get; set; }
public ClassB ClassBRequestObject { get; set; }
public ClassC ClassCRequestObject { get; set; }
public string ResponseData { get; set; }
public Exception Exception { get; set; }
}
That's fine - you just need to make it generic:
public class HttpConnectorRequest<T> where T: class
{
public int Id { get; set; }
public T RequestObject { get; set; }
public string ResponseData { get; set; }
public Exception Exception { get; set; }
}
Then you'd write something like:
var request = new HttpConnectorRequest<string>();
request.RequestObject = "Hello!";
Generics is a big topic - MSDN is probably a reasonable starting point, although I suspect you'll want to read about it in a tutorial or book at some point. (While my own book, C# in Depth, obviously covers generics, plenty of others do too :)
Note that this makes the whole type generic. If you want to make just a single property generic, you're out of luck... although you could make a method generic:
public class HttpConnectorRequest
{
// Other members elided
public void SetRequestObject<T>(T value) where T : class
{
...
}
public T GetRequestObject<T>() where T : class
{
...
}
}
Quite what this would do is up to you - bear in mind that someone could write:
var request = new HttpConnectorRequest();
request.SetRequestObject<string>("Hello");
var button = request.GetRequestObject<Button>();