Force multiple classes to have a specific shared member - vb.net

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

Related

Get parent class type from shared function

How do I find the class type that inherited the class running the shared function? Is this possible?
Public Class Class1
Inherits ClassFunctions
End Class
Public Class Class2
Inherits ClassFunctions
End Class
Public Class ClassFunctions
Public Shared Function GetParentName() As String
Dim name As String = ""
'?
Return name
End Function
End Class
Public Module Example
Public Name1 As String = Class1.GetParentName() 'Class1
Public Name2 As String = Class2.GetParentName() 'Class2
End Module
As the name Shared says, the Function is shared, but not inherited as would an instance method. Therefore, its parent type is always the original parent type (even when called from a derived class). This function always returns ClassFunctions even when called from Class1 or Class2:
'Failing attempt to return "inheriting" class name
Public Shared Function GetParentName() As String
Dim stack As New StackTrace
Dim className As String = stack.GetFrame(0).GetMethod.ReflectedType.Name
Return className
End Function
With an instance method you can write
'This works
Public Function GetParentName() As String
Return Me.GetType().Name
End Function
But you must call it from an object, and it returns the type name of this object.
A working solution with shared functions is to use Shadowing in Visual Basic, i.e., to hide the parent implementation and provide a new one. The disadvantage is that this mechanism does not automatically produce the desired class names. You must implement it explicitly in each class:
Public Class ClassFunctions
Public Shared Function GetParentName() As String
Return NameOf(ClassFunctions)
End Function
End Class
Public Class Class1
Inherits ClassFunctions
Public Shared Shadows Function GetParentName() As String
Return NameOf(Class1)
End Function
End Class
Public Class Class2
Inherits ClassFunctions
Public Shared Shadows Function GetParentName() As String
Return NameOf(Class2)
End Function
End Class
Instead of providing the name as string literal, I used the NameOf operator. The advantage over the former is that the correctness of the name is tested by the VB compiler.

Runtime import possible?

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.

Implementing inherited generic interface in vb.Net

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).

Optional Readonly Property in VB.Net Interface

I am trying to develop a simple interface for allowing quick lists to be generated from classes. Basically, the interface needs to return an ID and a Name. However, some classes have a calculated name property which is read only, others just use a read/write name property. Basically, all I care is that it has a getter, it does not matter if the property has a setter. How can I write this interface to handle either or without throwing compile errors?
I have read this question and didn't really follow it, maybe I am just dense. If so, please show me the error of my ways :)
Looks like the answer from the other question will work: here's a sample:
Public Interface IReadOnly
ReadOnly Property Name() As String
End Interface
Public Interface IReadWrite
Inherits IReadOnly
Overloads Property Name() As String
End Interface
Public Class ReadOnlyClass
Implements IReadOnly
Private _Name
Public ReadOnly Property Name() As String Implements IReadOnly.Name
Get
Return _Name
End Get
End Property
End Class
Public Class ReadWriteClass
Implements IReadWrite
Private ReadOnly Property ReadOnly_Name() As String Implements IReadOnly.Name
Get
Return Name
End Get
End Property
Private _Name As String
Public Overloads Property Name() As String Implements IReadWrite.Name
Get
Return _Name
End Get
Set(ByVal value As String)
_Name = value
End Set
End Property
End Class
The above approach will actually result in classes that implement IReadWrite also implementing IReadOnly--so you'll actually need to downcast to IReadWrite in order to set the property.
Another approach, which avoids that issue but requires a little more logic in the implementing classes and their caller's is something like:
Public Interface ISometimesWritable
Property Name() As String
ReadOnly Property AllowNameEdit() As Boolean
End Interface
Public Class ReadOnlyClass
Implements ISometimesWritable
Public ReadOnly Property AllowNameEdit() As Boolean Implements ISometimesWritable.AllowNameEdit
Get
Return False
End Get
End Property
Private _Name As String
Public Property Name() As String Implements ISometimesWritable.Name
Get
Return _Name
End Get
Set(ByVal value As String)
Throw New NotSupportedException("Name cannot be set when AllowNameEdit is False")
End Set
End Property
End Class
Public Class ReadWriteClass
Implements ISometimesWritable
Public ReadOnly Property AllowNameEdit() As Boolean Implements ISometimesWritable.AllowNameEdit
Get
Return True
End Get
End Property
Private _Name As String
Public Property Name() As String Implements ISometimesWritable.Name
Get
Return _Name
End Get
Set(ByVal value As String)
_Name = value
End Set
End Property
End Class
Update: To answer the question about downcasting; "downcasting" is a term used to describe casting an object from a superclass, interface, or abstract base class Type into a more concrete Type.
For example, the first example above defines two interfaces: IReadOnly and IReadWrite. You'll notice that IReadWrite implements IReadOnly, which means that you can make both IReadWrite and IReadOnly calls to objects which implement IReadWrite.
Since IReadWrite implements IReadOnly, IReadWrite is said to be a "sub-class" of IReadOnly (although "sub-class" is more accurately used to describe a class which inherits a base class, rather then implements an interface--for the sake of simplicity they are very nearly the same concept). If IReadWrite is a sub-class of IReadOnly, then the inverse is true--IReadOnly is a super-class of IReadWrite.
For example, I can describe an instance of ReadWriteClass as an implementation of either interface:
Public Sub SomeMethod()
dim readOnlyInstance as IReadOnly = new ReadWriteClass()
Console.WriteLine(readOnlyInstance.Name)
' The following line won't compile, since we're communicating with ReadWriteClass as an instance of IReadOnly
'readOnlyInstance.Name = "Santa Clause"
' Here we downcast the variable to reference it by it's other interface, IReadWrite
dim readWriteInstance = DirectCast(readOnlyInstance, IReadWrite)
' Now we can both Get and Set the value of Name
readWriteInstance.Name = "John Doe"
Console.WriteLine(readWriteInstance.Name)
' Note that in the above example we created *one* instance of ReadWriteClass
' and have provided two variables / references to the same underlying object.
Console.WriteLine(readOnlyInstance.Name) ' <-- note that this should return "John Doe"
End Sub

Can I inherit from a generic class without specifying a type?

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