I have below object model with simple inheritance:
public class RuntimeApiManagerBase
{
}
public class CatalogRuntimeApiManagerBase : RuntimeApiManagerBase
{
public void Method1()
{
}
}
public class DocumentRuntimeApiManagerBase : RuntimeApiManagerBase
{
public void Method2()
{
}
public void Method3()
{
}
}
public class BaseObject
{
public BaseObject(RuntimeApiManagerBase runtimeApiMgr)
{
RuntimeApiMgr = runtimeApiMgr;
}
public RuntimeApiManagerBase RuntimeApiMgr { get; set; }
}
public class Catalog : BaseObject
{
public Catalog() : base(new CatalogRuntimeApiManagerBase())
{
}
}
public class Document : BaseObject
{
public Document() : base(new DocumentRuntimeApiManagerBase())
{
}
}
Now, I want to access RuntimeApiMgr Property's Methods based on exactly derived type. However, it displays nothing which is logical:
Catalog c1 = new Catalog();
// c1.RuntimeApiMgr. => No Method1
Document d1 = new Document();
// d1.RuntimeApiMgr. => No Method2 and Method3
Is that possible using different structure like Generics or something else?
Thanks for your time.
Use generics:
public class BaseObject<T>
where T : RuntimeApiManagerBase
{
public BaseObject(T runtimeApiMgr)
{
RuntimeApiMgr = runtimeApiMgr;
}
public T RuntimeApiMgr { get; set; }
}
public class Catalog : BaseObject<CatalogRuntimeApiManagerBase>
{
public Catalog() : base(new CatalogRuntimeApiManagerBase())
{
}
}
public class Document : BaseObject<DocumentRuntimeApiManagerBase>
{
public Document() : base(new DocumentRuntimeApiManagerBase())
{
}
}
In that case your c1.RuntimeApiMgr will be of the type CatalogRuntimeApiManagerBase and will have Method1
You can use RuntimeApiManagerBase as generic and have a type constraint where T must be a subclass of RuntimeApiManagerBase
public class BaseObject<T> where T : RuntimeApiManagerBase
{
public BaseObject(T runtimeApiMgr)
{
RuntimeApiMgr = runtimeApiMgr;
}
public T RuntimeApiMgr { get; set; }
}
public class Catalog : BaseObject<CatalogRuntimeApiManagerBase>
{
public Catalog() : base(new CatalogRuntimeApiManagerBase())
{
}
}
public class Document : BaseObject<DocumentRuntimeApiManagerBase>
{
public Document() : base(new DocumentRuntimeApiManagerBase())
{
}
}
Catalog c1 = new Catalog();
c1.RuntimeApiMgr.Method1();
Document d1 = new Document();
d1.RuntimeApiMgr.Method2();
Alternative to solution with generics is an old fashioned approach with casting.
Maybe something like this:
Catalog c1 = new Catalog();
(c1.RuntimeApiMgr as CatalogRuntimeApiManagerBase).Method1();
Document d1 = new Document();
(d1.RuntimeApiMgr as DocumentRuntimeApiManagerBase).Method2();
Or create new properties with the same name in Caltalog and Document classes:
public class Catalog : BaseObject
{
public Catalog() : base(new CatalogRuntimeApiManagerBase())
{
}
public new CatalogRuntimeApiManagerBase RuntimeApiMgr { get; set; }
}
Related
I am trying to use the DataConnection class of linq2db in an abstract base class like so:
public abstract class BaseDbDao<T> : DataConnection where T : IDatabase
{
public string DBName => DBContext.Database;
public T DBContext { get; }
public DataConnection GetDataConnection()
{
return (this);
}
internal BaseDbDao(RelativityHelper helper, int workspaceId)
: base(GetDataProvider(), helper.GetDBContextNew(workspaceId).GetConnection())
{
DBContext = (T)helper.GetDBContextNew(workspaceId);
}
public static IDataProvider GetDataProvider()
{
LinqToDB.Common.Configuration.AvoidSpecificDataProviderAPI = true;
return new SqlServerDataProvider("", SqlServerVersion.v2017);
}
}
I am then calling it up from another class like so:
public class EdMeDBDao : BaseDbDao<MSSQLDBContext>
{
public ITable<EdMeTableRow> EdMeDatabase => GetTable<EdMeTableRow>();
public EdMeDBDao(RelativityHelper helper)
: base(helper, "123456")
{
}
public List<RelativityWorkspace> GetActiveProjects()
{
using (var db = GetDataConnection())
{
var a = GetTable<EdMeTableRow>().CaseName
};
}
}
However, I can't seem to call var a = GetTable<EdMeTableRow>().CaseName as ITable<EdMeTableRow> does not contain a definition for 'CaseName'
EdMeTableRow is defined as:
[Table(Name = "NuixWorkflowCases", Schema = "dbo")]
public class EdMeTableRow
{
public string CaseName { get; set; }
}
How can I access EdMeTableRow class from the EdMeDBDao class?
I do not know if I understood something wrong. But Visual Studio says that adding an item does not allow the conversion from ExporterTaskWorker<ExporterTypeMusic> to ExporterTaskWorker<IExporterType>. But ExporterTypeMusic implements the IExporterType interface.
What am I doing wrong?
public interface IExporterType
{
bool BrandChannelAssigning();
}
public class ExporterTypeMusic : IExporterType
{
public bool BrandChannelAssigning()
{
throw new System.NotImplementedException();
}
}
public class ExporterTaskWorker<T> : INotifyPropertyChanged where T : IExporterType
{
public Config TheConfig { get; set; }
public object SomeProperty { get; set; }
...
...
public ExporterTaskWorker(Config _config) {
}
}
public class SomeClass
{
public ObservableCollection<ExporterTaskWorker<IExporterType>> ExporterInstanceCollection { get; set; } = new ObservableCollection<ExporterTaskWorker<IExporterType>>();
public void SomeMethod()
{
Config theConfig = new Config();
ExporterTaskWorker<ExporterTypeMusic> exporterTaskWorker = new ExporterTaskWorker<ExporterTypeMusic>(theConfig);
ExporterInstanceCollection.Add(exporterTaskWorker);
}
}
For background I mainly program in Java and am trying to work with/learn generics in a C# project and got stuck.
Here is my problem. From the main method you can see I am trying to set the soldiers task, but I'm getting the error,
cannot convert from 'TaskHeal' to 'TaskBase<SoldierBase>'
It seems that this cast should work as TaskHeal is a child of TaskBase, but it doesn't. Here is my complete code:
public class Main {
static void main(string[] args) {
SoldierMedic myMedic = new SoldierMedic();
myMedic.setTask(new TaskHeal(myMedic)); // Problem!
}
}
public class SoldierBase {
private TaskBase<SoldierBase> currentTask;
public int status;
public void setTask(TaskBase<SoldierBase> newTask) {
this.currentTask = newTask;
}
}
public class SoldierMedic : SoldierBase {
public int healRate = 45;
}
public abstract class TaskBase<T> where T : SoldierBase {
protected T soldier;
public TaskBase(T unit) {
this.soldier = unit;
this.soldier.status = 1;
}
public abstract void preformTask();
}
public class TaskHeal : TaskBase<SoldierMedic> {
public TaskHeal(SoldierMedic unit) : base(unit) { }
public override void preformTask() {
this.soldier.healRate++;
}
}
If you don't mind having an additional base, non-generic class for TaskBase and SoldierBase, you could do this:
class Program
{
static void Main(string[] args)
{
var myMedic = new SoldierMedic();
myMedic.setTask(new TaskHeal(myMedic)); // Problem!
}
}
public class SoldierBase
{
public int status;
}
public class SoldierBase<T> : SoldierBase where T : SoldierBase
{
private TaskBase currentTask;
public void setTask(TaskBase newTask)
{
this.currentTask = newTask;
}
}
public class SoldierMedic : SoldierBase<SoldierMedic>
{
public int healRate = 45;
}
public abstract class TaskBase
{
}
public abstract class TaskBase<T> : TaskBase where T : SoldierBase<T>
{
protected T soldier;
public TaskBase(T unit)
{
this.soldier = unit;
this.soldier.status = 1;
}
public abstract void preformTask();
}
public class TaskHeal : TaskBase<SoldierMedic>
{
public TaskHeal(SoldierMedic unit) : base(unit) { }
public override void preformTask()
{
this.soldier.healRate++;
}
}
If you want this to look more like c# (using properties, proper access modifiers and casing), you'd do something like this:
class Program
{
static void Main(string[] args)
{
var myMedic = new SoldierMedic();
myMedic.CurrentTask = new TaskHeal(myMedic); // Problem!
}
}
public class SoldierBase
{
public int Status { get; set; }
}
public class SoldierBase<T> : SoldierBase where T : SoldierBase
{
public TaskBase CurrentTask { get; set; }
}
public class SoldierMedic : SoldierBase<SoldierMedic>
{
public int HealRate { get; set; } = 45;
}
public abstract class TaskBase
{
}
public abstract class TaskBase<T> : TaskBase where T : SoldierBase<T>
{
protected T Soldier;
public TaskBase(T unit)
{
Soldier = unit;
Soldier.Status = 1;
}
public abstract void PerformTask();
}
public class TaskHeal : TaskBase<SoldierMedic>
{
public TaskHeal(SoldierMedic unit) : base(unit) { }
public override void PerformTask()
{
Soldier.HealRate++;
}
}
I need to execute derived class constructor before base class constructor.
I am attaching code which is using virtual object in base class which need to be initialised in derived class. We decide type of virtual object in derived class and then assign values to that object once we have type of that object.
How could I call derived class constructor before base class constructor in this scenario.
public class BaseClass : UserControl, INotifyPropertyChanged
{
public Path ConnIn;
public Path ConnOut;
public virtual ObjectBase BaseObject { get; set; }
public void BaseClass(XmlElementConfig config)
{
this.BaseObject.Title = config.Title;
this.BaseObject.GroupID = config.GroupID;
}
}
public partial class DerivedClass : CanvasBase
{
private Audio_MonitorAction audio_objectAction;
public override ObjectBase BaseObject
{
get { return audio_objectAction; }
set
{
audio_objectAction = (Audio_MonitorAction)value;
NotifyPropertyChanged();
}
}
public DerivedClass(XmlElementConfig config) : base(config)
{
InitializeComponent();
audio_objectAction = new Audio_MonitorAction(createGuid);
}
}
"execute derived class constructor before base class constructor" is impossible
if possible, move initialization into BaseObject property (why should it be virtual?)
public class BaseClass : UserControl, INotifyPropertyChanged
{
public Path ConnIn;
public Path ConnOut;
private ObjectBase baseObject;
public ObjectBase BaseObject
{
get { return baseObject; }
set
{
baseObject = value;
if (baseObject != null)
{
baseObject.Title = config.Title;
baseObject.GroupID = config.GroupID;
}
NotifyPropertyChanged();
}
}
XmlElementConfig config;
public void BaseClass(XmlElementConfig config)
{
this.config = config;
}
}
public partial class DerivedClass : CanvasBase
{
private Audio_MonitorAction audio_objectAction;
public DerivedClass(XmlElementConfig config) : base(config)
{
InitializeComponent();
BaseObject = audio_objectAction = new Audio_MonitorAction(createGuid);
}
}
public class BaseClass : UserControl, INotifyPropertyChanged
{
public Path ConnIn;
public Path ConnOut;
public virtual ObjectBase BaseObject { get; set; }
public void BaseClass(XmlElementConfig config)
{
InitBase(config);
}
protected void InitBase(XmlElementConfig config)
{
if (BaseObject != null)
{
BaseObject.Title = config.Title;
BaseObject.GroupID = config.GroupID;
}
}
}
public DerivedClass(XmlElementConfig config) : base(config)
{
InitializeComponent();
audio_objectAction = new Audio_MonitorAction(createGuid);
InitBase(config);
}
Alternatively you can do in BaseClass call to virtual function and override it in derived class.
public class BaseClass : UserControl, INotifyPropertyChanged
{
public Path ConnIn;
public Path ConnOut;
public virtual ObjectBase BaseObject { get; set; }
public void BaseClass(XmlElementConfig config)
{
InitDerivedClass();
if (BaseObject != null)
{
BaseObject.Title = config.Title;
BaseObject.GroupID = config.GroupID;
}
}
protected virtual void InitDerivedClass() {}
}
public partial class DerivedClass : CanvasBase
{
private Audio_MonitorAction audio_objectAction;
public override ObjectBase BaseObject
{
get { return audio_objectAction; }
set
{
audio_objectAction = (Audio_MonitorAction)value;
NotifyPropertyChanged();
}
}
protected override ObjectBase InitDerivedClass
{
audio_objectAction = new Audio_MonitorAction(createGuid);
}
public DerivedClass(XmlElementConfig config) : base(config)
{
InitializeComponent();
}
}
When having the following scenario I am unhappy with the consuming code that is littered with the line
var queryResult = _queryDispatcher.Dispatch<CustomerByIdQuery, CustomerByIdQueryResult>(customerByIdQuery).Customer;
I would prefer to have the code work this way for the consumer:
var queryResult = _queryDispatcher.Dispatch(customerByIdQuery).Customer;
Is there a way to accomplish this using generics?
Here is the code
interface IQuery{}
interface IQueryResult{}
interface IQueryHandler<TQuery, TQueryResult> : where TQueryResult:IQueryResult where TQuery:IQuery
{
TQueryResult Execute(TQuery query);
}
interface IQueryDispatcher
{
TQueryResult Dispatch<TQuery, TQueryResult>(TQuery query) where TQuery:IQuery where TQueryResult:IQueryResult
}
class GenericQueryDispatcher : IQueryDispatcher
{
public TQueryResult Dispatch<TQuery, TQueryResult>(TQuery parms)
{
var queryHandler = queryRegistry.FindQueryHandlerFor(TQuery);
queryHandler.Execute
}
}
class CustomerByIdQuery : IQuery
{
public int Id { get; set; }
}
class CustomerByIdQueryResult : IQueryResult
{
public Customer {get; set;}
}
class CustomerByIdQueryHandler : IQueryHandler
{
public CustomerByIdQueryResult Execute(TQuery query)
{
var customer = _customerRepo.GetById(query.Id);
return new CustomerByIdQueryResult(){Customer = customer};
}
}
public class SomeClassThatControlsWorkFlow
{
IQueryDispatcher _queryDispatcher;
public SomeClassThatControlsWorkFlow(IQueryDispatcher queryDispatcher)
{
_queryDispatcher = queryDispatcher;
}
public void Run()
{
var customerByIdQuery = new CustomerByIdQuery(){Id=1};
//want to change this line
var customer = _queryDispatcher.Dispatch<CustomerByIdQuery, CustomerByIdQueryResult>(customerByIdQuery).Customer;
}
}
Here is what I would like to have :
public class ClassWithRunMethodIWouldLikeToHave
{
IQueryDispatcher _queryDispatcher;
public SomeClassThatControlsWorkFlow(IQueryDispatcher queryDispatcher)
{
_queryDispatcher = queryDispatcher;
}
public void Run()
{
var customerByIdQuery = new CustomerByIdQuery(){Id=1};
//want to change this line
var customer = _queryDispatcher.Dispatch(customerByIdQuery).Customer;
}
}
Yes, it's possible, but you have to make the Dispatch method's parameter generic (that way, the compiler can infer the type parameters from the method parameter). To do this, it looks like you'll first need a generic version of the IQuery and IQueryResult interfaces:
interface IQuery<TQuery, TQueryResult> : IQuery {}
interface IQueryResult<T> : IQueryResult
{
T Result { get; }
}
Next, make CustomerByIdQuery and CustomerByIdQueryResult implement the respective generic interfaces:
class CustomerByIdQuery : IQuery, IQuery<int, Customer>
{
public int Id { get; set; }
}
class CustomerByIdQueryResult : IQueryResult, IQueryResult<Customer>
{
public Customer Result {get; set;}
}
Now you can add an overload for Dispatch that accepts the generic parameter:
interface IQueryDispatcher
{
IQueryResult<TQueryResult> Dispatch<TQuery, TQueryResult>(IQuery<TQuery, TQueryResult> parms);
}
class GenericQueryDispatcher : IQueryDispatcher
{
public TQueryResult Dispatch<TQuery, TQueryResult>(IQuery<TQuery, TQueryResult> parms)
{
// TODO implement
}
}
The above will allow you to write:
var customerByIdQuery = new CustomerByIdQuery{Id=1};
var customer = _queryDispatcher.Dispatch(customerByIdQuery).Result;
I can't get rid of the cast, but this is working pretty close to what I want.
public interface IQueryDispatcher
{
TQueryResult Dispatch<TParameter, TQueryResult>(IQuery<TQueryResult> query)
where TParameter : IQuery<TQueryResult>
where TQueryResult : IQueryResult;
}
public interface IQueryHandler<in TQuery, out TQueryResult>
where TQuery : IQuery<TQueryResult>
where TQueryResult : IQueryResult
{
TQueryResult Retrieve(TQuery query);
}
public interface IQueryResult { }
public interface IQuery { }
public interface IQuery<TQueryResult> : IQuery { }
public class QueryDispatcher : IQueryDispatcher
{
readonly IQueryHandlerRegistry _queryRegistry;
public QueryDispatcher(IQueryHandlerRegistry queryRegistry)
{
_queryRegistry = queryRegistry;
}
public TQueryResult Dispatch<TQuery, TQueryResult>(IQuery<TQueryResult> query)
where TQuery : IQuery<TQueryResult>
where TQueryResult : IQueryResult
{
var handler = _queryRegistry.FindQueryHandlerFor<TQuery, TQueryResult>(query);
//CANT GET RID OF CAST
return handler.Retrieve((TQuery)query);
}
}
public interface IQueryHandlerRegistry
{
IQueryHandler<TQuery, TQueryResult> FindQueryHandlerFor<TQuery, TQueryResult>(IQuery<TQueryResult> query)
where TQuery : IQuery<TQueryResult>
where TQueryResult : IQueryResult;
}
public class GetCustByIdAndLocQuery : IQuery<CustByIdAndLocQueryResult>
{
public string CustName { get; set; }
public int LocationId { get; set; }
public GetCustByIdAndLocQuery(string name, int locationId)
{
CustName = name;
LocationId = locationId;
}
}
public class CustByIdAndLocQueryResult : IQueryResult
{
public Customer Customer { get; set; }
}
public class GetCustByIdAndLocQueryHandler : IQueryHandler<GetCustByIdAndLocQuery, CustByIdAndLocQueryResult>
{
readonly ICustomerGateway _customerGateway;
public GetCustByIdAndLocQueryHandler(ICustomerGateway customerGateway)
{
_customerGateway = customerGateway;
}
public CustByIdAndLocQueryResult Retrieve(GetCustByIdAndLocQuery query)
{
var customer = _customerGateway.GetAll()
.SingleOrDefault(x => x.LocationId == query.LocationId && x.CustomerName == query.CustName);
return new CustByIdAndLocQueryResult() { Customer = customer };
}
}
public interface ICustomerGateway
{
IEnumerable<Customer> GetAll();
}
public class Customer
{
public string CustomerName { get; set; }
public int LocationId { get; set; }
public bool HasInsurance { get; set; }
}