Creating base Attribute class with a generic property 'value' - c#

What I've done is created a base class of 'Attribute' in C#. From there I created other classes which inhert Attribute and add any additional properties as necessary. However when I try to create my observable collection which contains all these various attributes I get an underline here
private ObservableCollection<Attribute> _attributes;
under 'Attribute' saying: Using the generic type 'Attribute< TValue >' requires one type arguments. The reason for the base class of Attribute is so I can create multiple attributes as seen below.
Attribute Class
using System.Collections.Generic;
namespace ExampleTool.Model
{
public class Attribute<TValue>
{
public string Key { get; set; }
public TValue Value { get; set; }
}
public class FloatAttr : Attribute<float>
{
public string Label { get; set; }
private float minValue { get; set; }
private float maxValue { get; set; }
}
public class IntAttr : Attribute<int>
{
public string Label { get; set; }
private float minValue { get; set; }
private float maxValue { get; set; }
}
public class StringAttr : Attribute<string>
{
public string Label { get; set; }
}
public class BoolAttr : Attribute<bool>
{
public string Label { get; set; }
}
public class ListStringAttr : List<string>
{
public string Label { get; set; }
}
}
ViewModel - where error occurs...
using System.Collections.Generic;
using System.Collections.ObjectModel;
using ExampleTool.Model;
using ExampleTool.Helper;
namespace ExampleTool.ViewModel
{
public class AttributeViewModel : ObservableObject
{
private ObservableCollection<Attribute> _attributes;
public ObservableCollection<Attribute> Attributes
{
get { return _attributes; }
set
{
_attributes = value;
NotifyPropertyChanged("Attributes");
}
}
public AttributeViewModel()
{
//hard coded data for testing
Attributes = new ObservableCollection<Attribute>();
FloatAttr floatAttr = new FloatAttr();
Attributes.Add(floatAttr);
IntAttr intAttr = new IntAttr();
Attributes.Add(intAttr);
StringAttr stringAttr = new StringAttr();
Attributes.Add(stringAttr);
BoolAttr boolAttr = new BoolAttr();
Attributes.Add(boolAttr);
ListStringAttr listStringAttr = new ListStringAttr();
Attributes.Add(listStringAttr);
}
}
}
Solution idea #1
- simply remove the property of value from the base class and define it in each sub class.
public class Attribute
{
public string Key { get; set; }
}
public class FloatAttr : Attribute
{
public float Value { get; set; }
public string Label { get; set; }
private float minValue { get; set; }
private float maxValue { get; set; }
}
public class IntAttr : Attribute
{
public int Value { get; set; }
public string Label { get; set; }
private float minValue { get; set; }
private float maxValue { get; set; }
}
public class StringAttr : Attribute
{
public string Value { get; set; }
public string Label { get; set; }
}
public class BoolAttr : Attribute
{
public bool Value { get; set; }
public string Label { get; set; }
}
public class ListStringAttr : Attribute
{
public List<string> Value { get; set; }
public string Label { get; set; }
}

Your base Attribute class is a generic type, then you must add type argument to it's usage. But you can't add just T:
private ObservableCollection<Attribute<T>> _attributes;
because T is not your type parameter. You should add new non-generic base class:
public class AttributeBase
{
public string Key { get; set; }
}
public class Attribute<TValue> : AttributeBase
{
public TValue Value { get; set; }
}
And implement AttributeRetriever like in this question:
public Attribute<T> GetAttribute<T>() where T: DatabaseItem, new()
{
return _attributes.OfType(typeof(Attribute<T>)).FirstOrDefault as Attribute<T>;
}
Good news are that your WPF View can works fine without type parameter because Binding uses reflection. Then if you no need to have an access to your properties in code you no need to implement retriever too.

Related

Different parent classes map different subclasses problem

Profile.cs
public class TestConfigProfile : Profile
{
public TestConfigProfile()
{
CreateMap<BaseBO, BaseVO>();
CreateMap<A_BO, A_VO>();
CreateMap<SubBO1, SubVO1>();
}
public class A_BO
{
public BaseBO Sub { get; set; }
}
public class A_VO
{
public BaseVO Sub { get; set; }
}
public class BaseBO
{
public int Id { get; set; }
public string Name { get; set; }
}
public class BaseVO
{
public int Id { get; set; }
public string Name { get; set; }
}
public class SubBO1 : BaseBO
{
public int Size { get; set; }
}
public class SubVO1 : BaseVO
{
public int Size { get; set; }
}
}
test code like this...
public void TestConvert()
{
TestConfigProfile.A_BO bo = new TestConfigProfile.A_BO();
bo.Sub = new TestConfigProfile.SubBO1()
{
Id = 1,
Name = "SubBO1",
Size = 4421
};
TestConfigProfile.A_VO vo = _mapper.Map<TestConfigProfile.A_BO, TestConfigProfile.A_VO>(bo);
}
The result is as follows, but it does not meet my expectations, how can I configure this? Also I don't want to use a parent class.
Successfully mapped to a subclass.
With AutoMapper, mapping inheritance is opt-in.
Therefore, when you map from BaseBO to BaseVO, you need to include the derived mappings.
public TestConfigProfile()
{
CreateMap<BaseBO, BaseVO>()
.Include<SubBO1, SubVO1>(); // Include necessary derived mappings
CreateMap<A_BO, A_VO>();
CreateMap<SubBO1, SubVO1>();
}
See this working example.

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.

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.

How do I create a List<Item<T>>

Just trying to get my head around this one. Consider the following code.
public class Setting<T> where T: struct
{
public string Name { get; set; }
public T Value { get; set; }
}
public class SettingsDto
{
public List<Setting<>> Settings{ get; set; }
}
The issue is in the SettingsDto class. My Settings property wants me to specify a type for List>. All I know right now is that T must be a struct. How can I create a list of Settings such that I can add to it later - perhaps at runtime. Is there a way around this ?
Many thanks for all replies.
You can carry over the generic parameter over to SettingsDto:
public class SettingsDto<T> where T : struct
{
public List<Setting<T>> Settings { get; set; }
}
You must to pass T generic type to SettingsDto.
Try this:
public class Setting<T> where T: struct
{
public string Name { get; set; }
public T Value { get; set; }
}
public class SettingsDto<T> where T: struct
{
public List<Setting<T>> Settings { get; set; }
}
You can only do it if all settings have something in common. Consider using an interface like this:
public interface ISetting
{
string Name { get; set; }
}
public class Setting<T> : ISetting
where T : struct
{
public string Name { get; set; }
public T Value { get; set; }
}
public class SettingsDto
{
public List<ISetting> Settings { get; set; }
}
class Program
{
static void Main(string[] args)
{
var set=new SettingsDto();
set.Settings=new List<ISetting>();
set.Settings.Add(new Setting<int>() { Name="Setting1", Value=100 });
set.Settings.Add(new Setting<double>() { Name="Setting2", Value=Math.PI });
set.Settings.Add(new Setting<DateTime>() { Name="Setting3", Value=DateTime.Now });
set.Settings.Add(new Setting<int>() { Name="Setting4", Value=200 });
foreach(var setting in set.Settings.OfType<Setting<int>>())
{
Console.WriteLine("{0}={1}", setting.Name, setting.Value);
}
// Setting1=100
// Setting4=200
}
}
Does it has to be struct? Otherwise You could use
public List<Setting<object>> Settings { get; set; }
and cast it when needed.
If it is not for performance reasons, you could put the struct inside a class.

In C#, what is the best way to cast a generic type to its base class?

I have the following object:
public class ItemChange<T> where T : MyBase
{
public DateTime When { get; set; }
public string Who { get; set; }
public T NewState;
public T OldState;
}
and i am trying to cast and instance of ItemChange<T> to ItemChange<MyBase> but i am getting a cast exception.
Unable to cast object of type . . .
WHat is the proper way to cast a generic type to its base calss (assuming the where constraint above)
You can't do this directly since classes in C# do not support co-variance. You can do it with interfaces however:
public interface IItemChange<out T> where T : MyBase
{
DateTime When { get; set; }
string Who { get; set; }
T NewState { get; }
T OldState { get; }
}
public class ItemChange<T> : IItemChange<T> where T : MyBase
{
public DateTime When { get; set; }
public string Who { get; set; }
public T NewState { get; set; }
public T OldState { get; set; }
}
Then you can do:
IItemChange<MyBase> base = new ItemChange<MySubclass>();
for some subclass MySubclass of MyBase.
You need to define an explicit cast operator.
using System;
using System.Collections.Generic;
using System.Linq;
namespace brosell
{
public class MyBase
{
}
public class ItemChange<T> where T : MyBase
{
public DateTime When { get; set; }
public string Who { get; set; }
public T NewState;
public T OldState;
public static explicit operator ItemChange<MyBase>(ItemChange<T> i)
{
return new ItemChange<MyBase> { When = i.When, Who = i.Who, NewState = i.NewState, OldState = i.OldState};
}
}
public class Sub1: MyBase
{
}
public class Sub2: MyBase
{
}
public class HelloWorld
{
public static void Main(string[] args)
{
ItemChange<Sub1> item = new ItemChange<Sub1>();
ItemChange<MyBase> itemCasted = (ItemChange<MyBase>)item;
}
}
}

Categories

Resources