I will try to explain the problem as clearly as possible. I am not sure if what I need is possible but expect that there must be some solution to this. I wont be able to put in actual code here but will add whatever is needed to explain the problem.
Initially we had two separate classes as defined below.
Imports QMember48
Public Class Member48
Public Function ProcessInfo(reqctx as Member48.RequestContext, memid as String)
'Code here
End Function
Public Function UpdateInfo(partner as Member48.Partner, memid as String)
'Code here
End Function
'Other methods and functions come here
End Class
Imports QMember50
Public Class Member50
Public Function ProcessInfo(reqctx as Member50.RequestContext, memid as String)
'Code here
End Function
Public Function UpdateInfo(partner as Member50.Partner, memid as String)
'Code here
End Function
'Other methods and functions come here
End Class
Basically these two classes have common methods and functions but the references are different.
We next decided to create a factory pattern to get the objects of these classes based on some input parameter.
Our current implementation of code is like this:
'Base class definition
Imports QMember48
Public MustInherit Class Member
Public MustOverride Function ProcessInfo(reqctx as Member48.RequestContext, memid as String)
Public MustOverride Function UpdateInfo(partner as Member48.Partner, memid as String)
End Class
'Factory
Public Module MemFactory
Function GetMember(val as string) as Member
'Do some processing here
If val = "500" return new Member50() else return new Member48()
End Function
End Module
The problem is that the base class refers to Member48 and when the factory generates an object for Member50, the reference to Member48 still remains. This needs to be corrected somehow in the base class. If an object of Member50 is needed, there should not be any reference to Member48. But again how do we define the base class without any hardcoded import of Member48/50 ?
Is there any way to resolve this issue? If more details are needed, I can add later.
Thanks.
The problem is that both Members don't have any method in common. There may be two methods with the same name, but those methods have different signatures. You can neither pass a Member48.RequestContext to a Member50, nor can you pass a Member50.RequestContext to a Member48. That's why inheritance can't applied here reasonably. A common base class of both members would be empty.
You could use a generic base class as follows:
MustInherit Class Member(Of TRequest, TPartner)
Public MustOverride Function ProcessInfo(reqctx As TRequest, memid As String)
Public MustOverride Function UpdateInfo(partner As TPartner, memid As String)
End Class
Class Member48
Inherits Member(Of QMember48.RequestContext, QMember48.Partner)
'...
End Class
Class Member50
Inherits Member(Of QMember50.RequestContext, QMember50.Partner)
'...
End Class
However, this base class does not give you any advantage with a factory. The factory would have to return an untyped Member and you'll lose any advantage of the generic implementation.
Another approach is trying to unify the RequestContexts and Partners. If the members only rely on common members, this can be done very easily with an interface. If there are no common members, you could even use an empty interface (as a hint of the requested type) and do the following:
MustInherit Class Member
Public MustOverride Function ProcessInfo(reqctx As IRequestContext, memid As String)
'...
End Class
The concrete Member would be responsible for checking for the correct type and could throw an exception if the wrong type is being passed.
The last approach is to go without any base class and let the factory return an Object (or maybe an empty base class). This might be the cleanest solution because it does not introduce any artificial inheritance where there is none. Of course, you would have to do type checks each time you access the object, but that is the same with most other methods.
Finally, there is not really a nice solution for your problem. Maybe you could revise your architecture and see if there are smarter structures or if you really need the Members as listed above.
Create a baseclass or interface for RequestContext and Partner, and let Member48.RequestContext/Member50.RequestContext and Member48.Partner/Member50.Partner implement those interfaces:
Public Interface IRequestContext
End Interface
Public Interface IPartner
End Interface
Class Member48RequestContext
Implements IRequestContext
End Class
Class Member48Partner
Implements IPartner
End Class
Class Member50RequestContext
Implements IRequestContext
End Class
Class Member50Partner
Implements IPartner
End Class
Now you can create a base class that accepts an IRequestContest and an IPartner:
Public MustInherit Class Member
Public MustOverride Function ProcessInfo(reqctx as IRequestContext, memid as String)
Public MustOverride Function UpdateInfo(partner as IPartner, memid as String)
End Class
and the implementation of your concrete member classes look like
Public Class Member50
Inherits Member
Public Overrides Function ProcessInfo(reqctx as IRequestContext, memid as String)
End Function
Public Overrides Function UpdateInfo(partner as IPartner, memid as String)
End Function
End Class
If you want, you could also create another subclass using generics:
Public MustInherit Class Member(Of TRequest As IRequestContext, TPartner As IPartner)
Inherits Member
Public Overrides Function ProcessInfo(reqctx as IRequestContext, memid as String)
Return ProcessInfo(DirectCast(reqctx, TRequest), memid)
End Function
Public Overrides Function UpdateInfo(partner as IPartner, memid as String)
Return UpdateInfo(DirectCast(partner, TPartner), memid)
End Function
Public Overloads MustOverride Function ProcessInfo(reqctx as TRequest, memid as String)
Public Overloads MustOverride Function UpdateInfo(partner as TPartner, memid as String)
End Class
and an implementation:
Public Class Member48
Inherits Member(Of Member48RequestContext, Member48Partner)
Public Overrides Function ProcessInfo(reqctx as Member48RequestContext, memid as String)
End Function
Public Overrides Function UpdateInfo(partner as Member48Partner, memid as String)
End Function
End Class
Note how Member48 only accepts an Member48RequestContext , instead of any IRequestContext like Member50. It's up to you if you want this extra safety.
Your factory method stays the same:
Function GetMember(val as string) as Member
'Do some processing here
If val = "500" Then
Return New Member50()
Else
Return New Member48()
End If
End Function
However, your factory still needs a reference to both Member50 and Member48, since it creates the concrete classes.
Another approach is to use a DI-container like StructureMap, which is able to scan all your assemblies automatically for all types that implement Member(Of TRequest As IRequestContext, TPartner As IPartner), asign a key to each one, and return the correct instance ,e.g. Member50 if asked for a specifig key, e.g. 500. But a full blown example of such a configuration would be out of scope of this question/answer.
Related
I'm already searching for hours, but I cannot find any solution or even a possible way, that fits.
I need a way to enforce, that multiple classes have a shared member. This classes can not be inherits another class, because they are entity data model classes and i work on a partial class file beside the autogenerated files.
I tried to use an interface, but interfaces does not provide the option to declare a member as shared.
My try was like:
Public Interface IInterfaceA
ReadOnly Property PropA as String
End Interface
Public Partial Class ClassA
Implements IInterfaceA
Public Shared ReadOnly Property SPropA As String
Get
Return "FixedValueStringForClassA"
End Get
End Property
Public ReadOnly Property PropA As String Implements IInterfaceA.PropA
Get
Return SPropA
End Get
End Property
End Class
Public Partial Class ClassB
Implements IInterfaceA
Public Shared ReadOnly Property SPropA As String
Get
Return "FixedValueStringForClassB"
End Get
End Property
Public ReadOnly Property PropA As String Implements IInterfaceA.PropA
Get
Return SPropA
End Get
End Property
End Class
So I can call the PropA Member, when I get e.g. an array of types, that implements IInterfaceA.
But my first implementation does not enforce the shared property, only the normal property. To call the normal property, I would need an instance of that type, but this is not the way I searching for.
Maybe there is another solution to this problem.
Unfortunately you can't force a class to implement a shared member.
However, you CAN share extension methods from a module.
I had a similar problem recently with needing to share functions with multiple classes but still be able to run them as that class. Which I solved using extension methods.
Basically how it works is the module extends all objects that implement an interface, which can be conveniently included with the module, that interface will include anything the module will need access to from the calling class and the module will include anything that needs to be shared between all classes
Public Module SharedProp
Dim PropA As String
<Extension()>
Public Function GetPropA(Of T As IInterfaceA)(this As T) As String
Return PropA
End Function
<Extension()>
Public Sub SetPropA(Of T As IInterfaceA)(this As T, value As String)
PropA = value
End Function
Public Interface IInterfaceA
Property PropA As String
End Interface
End Module
so long as the module is included, any class which impliments your interface will have access to the module's extension methods, which in turn has access to the module's implicitly shared members
Imports SharedProp
Public Partial Class ClassA
Implements IInterfaceA
Public Property PropA As String Implements IInterfaceA.PropA
Get
Return Me.GetPropA()
End Get
Set(value As String)
Me.SetPropA(value)
End Set
End Property
End Class
Public Partial Class ClassB
Implements IInterfaceA
Public Property PropA As String Implements IInterfaceA.PropA
Get
Return Me.GetPropA()
End Get
Set(value As String)
Me.SetPropA(value)
End Set
End Property
End Class
Hope that helps solve your problem!
EDIT: Shared on Class basis instead of Interface
Public Module SharedProp
Dim PropA As Dictionary(Of Type, String)
<Extension()>
Public Function GetPropA(Of T As IInterfaceA)(this As T) As String
If PropA.ContainsKey(GetType(T)) Then
Return PropA(T)
Else
Return Nothing
End Function
<Extension()>
Public Sub SetPropA(Of T As IInterfaceA)(this As T, value As String) As String
If PropA.ContainsKey(GetType(T) Then
PropA(T) = value
Else
PropA.Add(GetType(T), value)
End If
End Function
Public Interface IInterfaceA
Property PropA As String
End Interface
End Module
Trying to get my head around generic interfaces and classes. How do I 'get T' when using my class in the new method and call data.method using this type?
Public MustInherit Class RepositoryBase(Of T)
Implements IRepository(Of T)
Private Data As IDAL
Public Sub New()
Data = DTOParserFactory.GetParser(T.GetType().ToString())
End Sub
Public Sub delete(BaseDTO As T) Implements Domain.Business.IRepository(Of T).delete
'Data.delete(Convert.ChangeType(BaseDTO, TypeOf(Type))
End Sub
Public Function getAll() As System.Linq.IQueryable(Of T) Implements Domain.Business.IRepository(Of T).getAll
'Return Data.getAll()()
End Function
End Class
I'm assuming you need to get the Type object for T?
In your constructor
Public Sub New()
Data = DTOParserFactory.GetParser(GetType(T).ToString())
End Sub
I'm not super clear on the question, but perhaps this is what you are looking for.
Assuming you have a common base class BaseDTO then you would define your RepositoryBase class like this:
Public MustInherit Class RepositoryBase(Of T As BaseDTO)
Then you declare an instace of the class like this:
Dim userRepository As New RepositoryBase(Of User)()
What this does is constrain T to be a subclass of BaseDTO, and gives you access to all of BaseDTO's methods.
In our current project I am using a generic interface iService which is inherited by all other service interfaces. For instance IService is inherited by ILogService.
The ILogService interface is then implemented by LogService as below:
Public Interface IService(Of T)
Sub Save(ByVal T As IEntity)
Sub Remove(ByVal T As IEntity)
Function FindBy(ByVal Id As Guid) As T
Function FindAll() As IEnumerable(Of T)
End Interface
Public Interface ILogService
Inherits IService(Of Log)
Function FindLogsByOwner(ByVal Owner As Guid, ByVal visibility As LogVisibility) As IList(Of Log)
Function FindAllLogsByVisibility(ByVal visibility As LogVisibility) As IList(Of Log)
Function FindAllLogsByType(ByVal type As LogType) As IList(Of Log)
End Interface
Public Class LogService
Implements ILogService
Public Function FindAll() As System.Collections.Generic.IEnumerable(Of Model.CSLS.Log) Implements Infrastructure.Domain.IService(Of Model.CSLS.Log).FindAll
End Function
Public Function FindBy(Id As System.Guid) As Model.CSLS.Log Implements Infrastructure.Domain.IService(Of Model.CSLS.Log).FindBy
End Function
Public Sub Remove(T As Infrastructure.Domain.IEntity) Implements Infrastructure.Domain.IService(Of Model.CSLS.Log).Remove
End Sub
Public Sub Save(T As Infrastructure.Domain.IEntity) Implements Infrastructure.Domain.IService(Of Model.CSLS.Log).Save
End Sub
Public Function FindAllLogsByType(type As Model.CSLS.LogType) As System.Collections.Generic.IList(Of Model.CSLS.Log) Implements Model.CSLS.ILogService.FindAllLogsByType
End Function
Public Function FindAllLogsByVisibility(visibility As Model.CSLS.LogVisibility) As System.Collections.Generic.IList(Of Model.CSLS.Log) Implements Model.CSLS.ILogService.FindAllLogsByVisibility
End Function
Public Function FindLogsByOwner(Owner As System.Guid, visibility As Model.CSLS.LogVisibility) As System.Collections.Generic.IList(Of Model.CSLS.Log) Implements Model.CSLS.ILogService.FindLogsByOwner
End Function
End Class
Help Needed: I am trying to understand that when I am implementing ILogService interface I still get the functions/subs in the LogService class containing:
method parameter T of Type IEntity instead of Log
How can I update the method signature so T is displayed as Log?
What am I doing wrong?
Are you talking about these?
Sub Save(ByVal T As IEntity)
Sub Remove(ByVal T As IEntity)
This is very confusing, because in the above methods T is the name of a method parameter, not a generic type parameter. It could just as easily have been foo or bar. In each case the type of T is IEntity.
If the intention here was that Save and Remove should each accept an argument of type T, but that type T must implement IEntity, this is how you would express that:
Public Interface IService(Of T As IEntity)
Sub Save(ByVal entity As T)
Sub Remove(ByVal entity As T)
Function FindBy(ByVal Id As Guid) As T
Function FindAll() As IEnumerable(Of T)
End Interface
#DanTao is correct if that is the intention. If, however, you just intended the name of the method parameter to be specified by the generic type, that is not possible.
However, you can specify any name you like in the implementing method, so you can use Log if you want, but you can't enforce it (and some FxCop rule will warn you haven't used the same parameter name as specified in the interface).
I have two classes:
Public Class Subscribing
Private _subscribingObjects As IList(Of String)
Public Sub Add(ByVal obj As SubscribeObject)
'...code...'
End Sub
Public Sub Remove(ByVal index As Integer)
'...code...'
End Sub
End Class
Public Class Providing
Private _providingObjects As IList(Of String)
Public Sub Add(ByVal obj As ProvideObject)
'...code...'
End Sub
Public Sub Remove(ByVal index As Integer)
'...code...'
End Sub
End Class
Is there a more elegant way to add do this? One class would suffice, but since the Add methods have different arguments, then one really wouldn't work.
Any help would be appreciated.
this?
Public Class SubscribingProviding(Of t)
Private _subscribingObjects As IList(Of String)
Public Sub Add(ByVal obj As t)
'...code...'
End Sub
Public Sub Remove(ByVal index As Integer)
'...code...'
End Sub
End Class
Your add functions should be fine. As long as you have different variable types being passed in you can have the function names be the same. Your remove Subs will not be allowed in the same class because it is using the same parameter Integer.
Eh.. probably not. They are different enough that you cant even Interface them.
I personally wouldn't mix the two responsibilities (of subscribing and providing) in one class. The classes themselves can easily be simplified by just inheriting from List(Of T)
Public Class Subscribing
Inherits List(Of SubscribeObject)
End Class
Public Class Providing
Inherits List(Of ProvideObject)
End Class
If you really want to get down to one class and make sure that it can only accept SubscribeObject and ProvideObject respectively, implement a common interface in both SubscribeObject and ProvideObject. Then create a generic class that accepts the interface:
' Common interface '
Public Interface ISubscribeProvideObject
End Interface
' SubscribeObject and ProvideObject both implementing the common interface '
Public Class SubscribeObject
Implements ISubscribeProvideObject
'...'
End Class
Public Class ProvideObject
Implements ISubscribeProvideObject
'...'
End Class
' Generic class accepting both types '
Public Class SubscribingProviding(Of T As ISubscribeProvideObject)
Inherits List(Of T)
'... Add() and Remove() methods only needed if custom logic applies ...'
End Class
I have the following sample code in a VB.NET console application. It compiles and works, but feels like a hack. Is there a way to define EmptyChild so that it inherits from Intermediate(Of T As Class) without using the dummy EmptyClass?
Module Module1
Sub Main()
Dim Child1 = New RealChild()
Child1.Content = New RealClass()
Dim Child2 = New EmptyChild()
Console.WriteLine("RealChild says: " & Child1.Test)
Console.WriteLine("EmptyChild says: " & Child2.Test)
Console.ReadLine()
End Sub
Public Class EmptyClass
End Class
Public Class RealClass
Public Overrides Function ToString() As String
Return "This is the RealClass"
End Function
End Class
Public MustInherit Class Base(Of T As Class)
Private _content As T = Nothing
Public Property Content() As T
Get
Return _content
End Get
Set(ByVal value As T)
_content = value
End Set
End Property
Public Overridable Function Test() As String
If Me._content IsNot Nothing Then
Return Me._content.ToString
Else
Return "Content not initialized."
End If
End Function
End Class
Public MustInherit Class Intermediate(Of T As Class)
Inherits Base(Of T)
'some methods/properties here needed by Child classes
End Class
Public Class RealChild
Inherits Intermediate(Of RealClass)
'This class needs all functionality from Intermediate.
End Class
Public Class EmptyChild
Inherits Intermediate(Of EmptyClass)
'This class needs some functionality from Intermediate,
' but not the Content as T property.
Public Overrides Function Test() As String
Return "We don't care about Content property or Type T here."
End Function
End Class
End Module
The other way to do this would be to move the generic code out of the Base class and then create 2 Intermediate classes like this:
Public MustInherit Class Intermediate
Inherits Base
'some methods/properties here needed by Child classes
End Class
Public MustInherit Class Intermediate(Of T As Class)
Inherits Intermediate
'implement generic Content property here
End Class
Then RealChild would inherit from the generic Intermediate and EmptyChild would inherit from the non-generic Intermediate. My problem with that solution is that the Base class is in a separate assembly and I need to keep the code that handles the generic type in that assembly. And there is functionality in the Intermediate class that does not belong in the assembly with the Base class.
Yes, you need to specify a type parameter when you inherit, or your EmptyChild must be generic as well. But, you don't have to dummy up a EmptyClass - just use Object as your type parameter:
Public Class EmptyClass
Inherits Intermediate(Of Object)
End Class