How to access Property of generic member in generic class [duplicate] - c#

This question already has answers here:
Accessing properties through Generic type parameter
(2 answers)
Closed 6 years ago.
I am newbie in C#. I am trying to create a Generic class. I have three classes and a Main/Generic class.
Three Classes
public class A
{
public string Name { get; set; }
public string Address { get; set; }
public A(string _name, string _address)
{
Name = _name;
Address = _address;
}
}
public class B
{
public string Name { get; set; }
public string Address { get; set; }
public B(string _name, string _address)
{
Name = _name;
Address = _address;
}
}
public class C
{
public string Name { get; set; }
public string Address { get; set; }
public C(string _name, string _address)
{
Name = _name;
Address = _address;
}
}
Generic Class
public class GenericClass<T>
{
public GenericClass(T obj)
{
DynamicObject = obj;
}
public T DynamicObject { get; set; }
}
I have successfully created a Generic class.
class Program
{
static void Main(string[] args)
{
A objA = new A("Mohit", "India");
GenericClass<A> objGenericClass = new GenericClass<A>(objA);
Console.ReadLine();
}
}
Now, if I need to use Class A/B/C property in the Generic class. How can I use it? I know that class reference type decide on the runtime. So, I can't use it in below way.But, Is there any other way?
public class GenericClass<T>
{
public GenericClass(T obj)
{
DynamicObject = obj;
}
public T DynamicObject { get; set; }
public void UseClassPro()
{
Console.WriteLine("Address " + DynamicObject.Address);//Compile time error here.
}
}

The Other Answers are right, but...
I just want to point out: while those other answers promote valid C# code, they make the generic aspect of you implementation superflous. You don't need generics anymore:
Given a base class or interface like
public interface IHasAddress
{
string Address { get; }
}
you don't need a Generic class anymore for what you are trying to achive (from what i can tell by the code you provided):
public class NotSoGenericClass
{
public GenericClass(IHasAddress obj)
{
DynamicObject = obj;
}
public IHasAddress DynamicObject { get; set; }
}
So as you can see, you can easily implement the desired behaviour w/o generics.
For you as a Beginner, i'd recommend the following basic rules when it comes to generics:
When you think you have to use generics, force yourself to consider abstraction via interfaces, abstract classes or base classes first. This often leads to simpler and cleaner solutions.
Same goes with Reflection. When you think you need Reflection, consider generics (Rule 1 is valid at that point to)
But Nothings wrong with Generics, its just more complex and often not needed. Compare the class above with the generic solution:
public class GenericClass<T> where T : IHasAddress // just for the sake of generics
{
public GenericClass(T obj)
{
DynamicObject = obj;
}
public T DynamicObject { get; set; }
}
Looks more complex and doesn't add any benefit, does it? Also note that you need a Interface/baseclass no matter what. Otherwise, you could also use Reflection (not recommended).
To actually answer your question
The precise answer to your question is:
You have to define that you generic parameter has to be assignable to IHasAddress using
public class GenericClass<T> where T : IHasAddress
^^^^^^^^^^^^^^^^^^^^^
This Part
This way, the compiler knows that T inherits or is of type IHasAddress or what ever you define. You can also pass multiple types at this place which adds mor flexibility when it comes to designing your interfaces.
Maybe, there are points to consider in your usecase which are not obvious from the information you provided in the question. In that case, feel free to add some details and i'll be happy to deep dive into those as well.

define interface:
public interface IABC
{
string Name { get; set; }
string Address { get; set; }
}
and in your generic class definition specify this interface:
public class GenericClass<T> where T: IABC
{
public GenericClass(T obj)
{
DynamicObject = obj;
}
public IABC DynamicObject { get; set; }
}
All your 3 classes implemet this interface:
public class A : IABC
public class B : IABC
public class C : IABC
After that you could call properties of IABC
A objA = new A("Mohit", "India");
GenericClass<A> objGenericClass = new GenericClass<A>(objA);
var adress = objGenericClass.DynamicObject.Address;

If you have properties in your generic arguments that share type and name, use a base class:
public class Base
{
public string Address { get; set; }
public A(string _address)
{
Address = _address;
}
}
public class A : Base
{
public string Name { get; set; }
public A(string _name, string _address) : base(_address)
{
Name = _name;
}
}
public class B : Base
{
public string Name { get; set; }
public B(string _name, string _address) : base(_address)
{
Name = _name;
}
}
public class C : Base
{
public string Name { get; set; }
public C(string _name, string _address) : base(_address)
{
Name = _name;
}
}
You can then use the base class as a constraint to your generic class:
public class GenericClass<T> where T : Base
{
public GenericClass(T obj)
{
DynamicObject = obj;
}
public T DynamicObject { get; set; }
public void UseClassPro()
{
Console.WriteLine("Address " + DynamicObject.Address);//Compiles now
}
}
If you can not use a base class, use an interface:
public interface IBase
{
string Address { get; set; }
}
public class A : IBase
{
public string Name { get; set; }
public string Address { get; set; }
public A(string _name, string _address)
{
Name = _name;
Address = _address;
}
}
public class B : IBase
{
public string Name { get; set; }
public string Address { get; set; }
public B(string _name, string _address)
{
Name = _name;
Address = _address;
}
}
public class C : IBase
{
public string Name { get; set; }
public string Address { get; set; }
public C(string _name, string _address)
{
Name = _name;
Address = _address;
}
}
public class GenericClass<T> where T : IBase
{
public GenericClass(T obj)
{
DynamicObject = obj;
}
public T DynamicObject { get; set; }
public void UseClassPro()
{
Console.WriteLine("Address " + DynamicObject.Address);//Compiles now
}
}

There is no 'clean' way of getting a property from a generic class without inheritance, either through an interface or a base class, as shown in the other answers.
If you don't want to use inheritance, you can use reflection, although this is far less efficient than using an inherited class.
// the generic class
public class GenericClass<T> where T: IABC
{
public GenericClass(T obj)
{
DynamicObject = obj;
}
public IABC DynamicObject { get; set; }
public T GetPropertyValue<T>(string propertyName)
{
var obj = GetType().GetProperty(propertyName).GetValue(this);
return (T)Convert.ChangeType(obj, typeof(T))
}
}
A objA = new A("Mohit", "India");
GenericClass<A> objGenericClass = new GenericClass<A>(objA);
var address = objGenericClass.GetPropertyValue<string>("address");
I stress that this is an alternative to inheritance that will not be very fast, but it might suit your needs.

Add an interface that implements the similar properties like the following :
public interface IInfo
{
public string Name { get; set; }
public string Address { get; set; }
}
public class GenericClass<T> where T : IInfo
{
public GenericClass(T obj)
{
DynamicObject = obj;
}
public T DynamicObject { get; set; }
public void UseClassPro()
{
Console.WriteLine("Address " + DynamicObject.Address);
}
}

Related

Derived Objected Cannot be Converted to "base object" in Delegate Handler

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.

Is there any way to make each class that implements interface Ae has a List of one of the concrete classes that implement Ba as property?

I have an interface (Ae) that has a list of objects (List) from another interface (Ba).
I have a class that implements interface Ae.
I have several classes that implement the Ba interface.
Is there any way to make each class that implements interface Ae has a List of one of the concrete classes that implement Ba as property?
public interface IQuestion
{
IAnswerOption[] Answers { get; set; }
}
public interface IAnswerOption
{
int Id { get; }
bool IsCorrect { get; set; }
}
public class AnswerOptionText : IAnswerOption
{
public int Id { get; }
public bool isCorrect;
public string ansText;
}
public class AnswerOptionImage : IAnswerOption
{
public int Id { get; }
public bool isCorrect;
public string imgSlug;
}
public class AudioQuestion : IQuestion
{
public AnswerOptionImage[] Answers;
public string audioName;
}
public class TextQuestion : IQuestion
{
public AnswerOptionText[] Answers { get; set; }
public string questionText { get; set; }
}
When I try it, AudioQuestion and TextQuestion doesn't allow me to use AnswerOptionImage[] and AnswerOptionText[] respectively.
Visual Studio says that I need to implement interface member IQuestion.Answers, but this is not what I intend.
If someone can help me I would be very grateful. Thanks.
It seems that using generics for your IQuestion interface will be a good fit:
public interface IQuestion<T> where T: IAnswerOption
{
T[] Answers { get; set; }
}
public class AudioQuestion : IQuestion<AnswerOptionImage>
{
public AnswerOptionImage[] Answers{ get; set; }
public string audioName;
}
public class TextQuestion : IQuestion<AnswerOptionText>
{
public AnswerOptionText[] Answers { get; set; }
public string questionText { get; set; }
}

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.

Can I create Interface Properties as Generic?

Is there anyway to create such interface which generates properties as generic.
public interface IInterface<T>
{
string nameOf(T)+"_Email" { get; set; } // this won`t compile
string nameOf(T)+"_Phone" { get; set; } // this won`t compile
}
public class Person
{
}
public class Details : IInterface<Person>
{
public string Person_Email { get; set; }
public string Person_Phone { get; set; }
}
I asked above question because my problem was as follow. I want to secure two classes with Interface contract. Then I combine these two classes in a ViewModel. Viewmodel is not really helping because I need these properties on Razor. Please see below.
public interface IPerson
{
string Email { get; set; }
string Phone { get; set; }
}
public interface IHotel
{
string Email { get; set; }
string Phone { get; set; }
}
public class Person : IPerson
{
public string Email { get; set; }
public string Phone { get; set; }
}
public class Hotel: IHotel
{
public string Email { get; set; }
public string Phone { get; set; }
}
public class ViewModel1 : IPerson, IHotel
{
//
// This is missing either Person or Hotel details
//
public string Email { get ; set ; }
public string Phone { get ; set ; }
}
public class ViewModel2 : IPerson, IHotel
{
//
// This is ok but PUBLIC modifier is not allowed, I cannot use.
//
string IPerson.Email { get ; set ; } // public modifier not allowed
string IHotel.Email { get ; set ; } // public modifier not allowed
string IPerson.Phone { get ; set ; } // public modifier not allowed
string IHotel.Phone { get ; set ; } // public modifier not allowed
}
No. It is not possible to dynamically modify the names of interface members with a class level generic argument.
Generics are designed to enable you to re-use the same functionality regardless of which generic type is specified. This is only possible if the interface remains consistent.
Consider this dilemma for example:
public class Foo<T>
{
public string GetPhone(IInterface<T> bar)
{
// how would I know what method to call on foo here?
return bar.????_Phone;
}
}
Below is an example of how you can have generic properties in an interface. You need a base class to translate for you and to bind the type you are looking for
public interface IInterface<T> where T : Contact
{
string Email { get; }
string Phone { get; }
}
public class Person : Contact
{
public string Phone { get; set; }
public string Email { get; set; }
}
public class DetailsBase<T> : IInterface<T> where T : Contact
{
Contact _cont { get; set; }
public string Email { get { return _cont.Email; } }
public string Phone { get { return _cont.Phone; } }
public DetailsBase(Contact cont)
{
_cont = cont;
}
}
public class Contact
{
public string Email { get; set; }
public string Phone { get; set; }
}
public class PersonDetails : DetailsBase<Person>
{
public PersonDetails(Person person) : base(person)
{
}
}

Specialize type when hiding property in subclass c#

i need an help on a really easy question about oop .net design.
I've an abstracted class:
public abstract class DataExtractionMethodConfig
{
public abstract DataExtractionMethod DataExtractionMethod { get; }
public object DataExtractionConfig { get; protected set; }
}
where DataExtractionConfig is a simple object that will be used in subclass to contains the configuration details. For example the OdbcExtractionConfig will be
public class OdbcExtractionConfig
{
public string OdbcName { get; set; }
public string BarcodeFilterExpression { get; set; }
public string ConnectionString { get; set; }
public string SqlCommand { get; set; }
public Enum.DatabaseServer DatabaseType { get; set; }
public List<string> KeyColumns { get; set; }
}
and the derived concrete class is
public class OdbcDataExtractionMethod : DataExtractionMethodConfig
{
private OdbcDataExtractionMethod(){}
public OdbcDataExtractionMethod(OdbcExtractionConfig config)
{
this.DataExtractionConfig = config;
}
public override DataExtractionMethod DataExtractionMethod { get { return DataExtractionMethod.ODBC; } }
}
How can i refactor the code to have DataExtractionConfig typed and not a simple object but mantaining the capability to extend the abstract class ?
How can i refactor the code to have DataExtractionConfig typed and not a simple object
By using Generics, but I warn you it can sometimes be a rabbit hole! Meaning that once you start down it, you get deeper and deeper, and things get more and more complex as you go.
public abstract class DataExtractionMethodConfig<T>
{
public abstract DataExtractionMethod DataExtractionMethod { get; }
public T DataExtractionConfig { get; protected set; }
}
And then
public class OdbcDataExtractionMethod : DataExtractionMethodConfig<OdbcExtractionConfig>
{ ... }

Categories

Resources