Optional Readonly Property in VB.Net Interface - vb.net

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

Related

Overload resolution works for normal method but not for constructor

My goal is to have a series of overloads, where the correct version of a method gets called depending on the type of the parameter (known only at runtime). However, I've run into an interesting problem in a case where the method I want to overload is a constructor.
Take the following inheritance structure:
Public MustInherit Class A
Public Property Common As String
End Class
Public Class X
Inherits A
Public Property Unique1 As String
Public Property Unique2 As String
End Class
Public Class Y
Inherits A
Public Property Unique3 As String
Public Property Unique4 As String
End Class
Base class A is inherited by both X and Y.
Now take this class which I'll use to show the problem:
Public Class Foo
Public Sub New(v As X)
Common = v.Common
Prop1 = v.Unique1
Prop2 = v.Unique2
Prop3 = "Some value"
Prop3 = String.Empty
End Sub
Public Sub New(v As Y)
Common = v.Common
Prop1 = "Some value"
Prop2 = String.Empty
Prop3 = v.Unique3
Prop4 = v.Unique4
End Sub
Public ReadOnly Property Common As String
Public ReadOnly Property Prop1 As String
Public ReadOnly Property Prop2 As String
Public ReadOnly Property Prop3 As String
Public ReadOnly Property Prop4 As String
Public Shared Sub Bar(v As X)
End Sub
Public Shared Sub Bar(v As Y)
End Sub
End Class
There is a normal method Bar with an overload, and also a constructor New with an overload. The first New has the same signature as the first Bar, and the second New has the same signature of the second Bar.
Finally take this test code:
Public Sub Test()
Dim Param As Object = New X
'This works fine
Foo.Bar(Param)
'This gives a compile error
Dim Thing As New Foo(Param)
End Sub
The compiler seems to have no problem with the call to Bar, but for the constructor call I get the following compile error:
Overload resolution failed because no accessible 'New' can be called without a narrowing conversion:
'Public Sub New(v As X)': Argument matching parameter 'v' narrows from 'Object' to 'X'.
'Public Sub New(v As Y)': Argument matching parameter 'v' narrows from 'Object' to 'Y'.
Why does the constructor call cause an error while the call to Bar does not.
Also, if I change the Param declaration to Dim Param As A = New X, then neither of them will compile.
I feel like I should understand this one, but for whatever reason I don't. Could someone fill me in on why this doesn't work, and maybe suggest a work-around?
While it's still unclear exactly what you are trying to achieve, an Answer is the only reasonable place to share code. Here is an attempt at solving your problem with Option Strict On, using an interface to define the properties that a class must have in order to be passed to a Foo for its construction.
Note the comments in the code which also help explain things.
This abstracts things so that Foo doesn't have to know about all the types derived from A - it only knows about the interface. In fact, it 'inverts' the relationship so that A and its derived types know what is necessary for Foo (per the interface). The rest is the implementation of X and Y, where the definitions of Props 1 through 4 now live (instead of in the various overloaded Foo constructors). This collapses the number of constructors for Foo down to just one.
The logic for translating properties of a class derived from A into Foo's properties has to live somewhere. By pushing this logic out of Foo and instead into the derived classes, you can avoid the narrowing issues that Option Strict Off defers until runtime. In addition, adding a new Z class derived from A is easy, and you don't have to modify Foo to immediately use it.
But again, as it's not perfectly clear what you intend to do with this sample code, it's hard to know if this approach 'works' for what you are thinking of, or not.
Option Strict On
Module Module1
Sub Main()
Dim Param As A = New X
Dim Thing As New Foo(Param)
Param = New Y
Thing = New Foo(Param)
'if you make a new class Z which Inherits A, it will immediately be translatable to Foo
'albeit with all String.Empty properties unless you override the properties from A
End Sub
End Module
'Defines what a Foo wants, what a Foo needs
Public Interface IPropertiesForFoo
ReadOnly Property Common As String
ReadOnly Property Prop1 As String
ReadOnly Property Prop2 As String
ReadOnly Property Prop3 As String
ReadOnly Property Prop4 As String
End Interface
Public MustInherit Class A
Implements IPropertiesForFoo
Public Property Common As String
#Region "IPropertiesForFoo implementation"
'these are Overridable, so derived classes can choose what to change and what not to
'note these are all Protected, so only derived classes know about them. Users of A may not care.
'This is just one choice;
' you could also use Throw New NotImplementedException (instead of Return String.Empty)
' and force derived classes to handle every property
Protected Overridable ReadOnly Property IPropertiesForFoo_Prop1 As String Implements IPropertiesForFoo.Prop1
Get
Return String.Empty
End Get
End Property
Protected Overridable ReadOnly Property IPropertiesForFoo_Prop2 As String Implements IPropertiesForFoo.Prop2
Get
Return String.Empty
End Get
End Property
Protected Overridable ReadOnly Property IPropertiesForFoo_Prop3 As String Implements IPropertiesForFoo.Prop3
Get
Return String.Empty
End Get
End Property
Protected Overridable ReadOnly Property IPropertiesForFoo_Prop4 As String Implements IPropertiesForFoo.Prop4
Get
Return String.Empty
End Get
End Property
'private, and doesn't need to be Overridable, as Common can map directly
Private ReadOnly Property IPropertiesForFoo_Common As String Implements IPropertiesForFoo.Common
Get
Return Common
End Get
End Property
#End Region
End Class
Public Class X
Inherits A
Public Property Unique1 As String
Public Property Unique2 As String
Protected Overrides ReadOnly Property IPropertiesForFoo_Prop1 As String
Get
Return Unique1
End Get
End Property
Protected Overrides ReadOnly Property IPropertiesForFoo_Prop2 As String
Get
Return Unique2
End Get
End Property
Protected Overrides ReadOnly Property IPropertiesForFoo_Prop3 As String
Get
Return "Some value"
End Get
End Property
'doesn't need to override Prop4; leave it as String.Empty
End Class
Public Class Y
Inherits A
Public Property Unique3 As String
Public Property Unique4 As String
Protected Overrides ReadOnly Property IPropertiesForFoo_Prop1 As String
Get
Return "Some value"
End Get
End Property
'doesn't need to override Prop2; leave it as String.Empty
Protected Overrides ReadOnly Property IPropertiesForFoo_Prop3 As String
Get
Return Unique3
End Get
End Property
Protected Overrides ReadOnly Property IPropertiesForFoo_Prop4 As String
Get
Return Unique4
End Get
End Property
End Class
Public Class Foo
Public Sub New(v As IPropertiesForFoo)
Common = v.Common
Prop1 = v.Prop1
Prop2 = v.Prop2
Prop3 = v.Prop3
Prop4 = v.Prop4
End Sub
Public ReadOnly Property Common As String
Public ReadOnly Property Prop1 As String
Public ReadOnly Property Prop2 As String
Public ReadOnly Property Prop3 As String
Public ReadOnly Property Prop4 As String
End Class
For that matter, depending on what the rest of Foo actually does, you may not even need Foo - just pass around your instances of A, since they are also IPropertiesForFoo. Then pull out their properties labeled as Prop1, Prop2, as needed. (Again, your simplified sample source doesn't hint at the larger context enough to know if this approach fits well, or not.)
I'm sure I'll get plenty of people telling me again that I shouldn't be doing things this way, but here's what I did to actually solve the problem as I needed it:
Public Class Foo
Public Sub New(v As X)
End Sub
Public Sub New(v As Y)
End Sub
Public Shared Function Create(v As X) As Foo
Return New Foo(v)
End Function
Public Shared Function Bar(v As Y) As Foo
Return New Foo(v)
End Function
End Class
Which lets me use Foo like this:
Dim Param As Object = New Y
Foo.Create(Param)
Yes, the above uses late binding and loosely-typed code. But it also keeps redundant code to a minimum, requires no lengthy interface definition or implementation, is still completely predictable, and does exactly what I want. I do consider features like this useful and valid when used in the right context.
I do still wish I could get some answer as to why the overload resolution works with a normal method but not a constructor. But for now I guess I'll just have to settle for this work-around.

Retrieve object name

I need to retrieve the name of an instanced object (not the type name...)
I have seen that the GetProperties() function gets the child properties name but i need the name of the current object
Public Class Class1
Private mValore As String
Public Property Valore As String
Get
Return mValore
End Get
Set(value As String)
mValore = value
End Set
End Property
End Class
Public Class Class2
Private mMickey As new Class1
Public Property Mickey As Class1
Get
Return mMickey
End Get
Set(value As Class1)
mMickey = value
End Set
End Property
End Class
I need to obtain inside Class1 the name of instanced object in Class2: "Mickey"
Is it possible ?
Thanks in advice for all that will answer me.
As mentioned by Hans Passant, objects don't have names.
So if you really need names, you may introduce them, as a property or field. You may employ CallerMemberNameAttribute to automatically pass the caller name to e.g. constructor.
Another thing, objects might be created outside Class2, indeed in the Mickey ... Set setter you are assigning mMickey field to an object from somewhere outside, so the object might have a different name. I would prefer to create a copy of object instead of just assignment, then we can assign any name to it and it will not collide with the previous name. An example could be:
Imports System.Runtime.CompilerServices
Public Class Class1
Private mValore As String
Public ReadOnly Name As String
Public Sub New(mValore As String, <CallerMemberName> Optional callerMemberName As String = Nothing)
Me.mValore = mValore
Me.Name = callerMemberName
End Sub
Public ReadOnly Property Valore As String
Get
Return mValore
End Get
End Property
End Class
Public Class Class2
Private mMickey As Class1
Public Property Mickey As Class1
Get
Return mMickey
End Get
Set(value As Class1)
mMickey = New Class1(mValore:=value.Valore)
End Set
End Property
End Class
If Class2 only has one property, you can just get the only property's name
Public Class Class1
Public ReadOnly Property Valore As String
Get
Return GetType(Class2).GetProperties().Single().Name
End Get
End Property
End Class
Public Class Class2
Public Property Mickey As Class1
End Class
Or if it has multiple properties, you can just get the first property's name
Public Class Class1
Public ReadOnly Property Valore As String
Get
Return GetType(Class2).GetProperties().First().Name
End Get
End Property
End Class
Public Class Class2
Public Property Mickey As Class1
Public Property Mouse As String
End Class
That returns the first property in order in which the properties are defined. So if the order is changed, it breaks.
Surely there must be more qualifying information to lead us to a solution. Can I make the assumption that you are only interested in the name of the property whose type is Class1? Then you can also filter on the property's type
Public Class Class1
Public ReadOnly Property Valore As String
Get
Return GetType(Class2).GetProperties().Where(Function(pi) pi.PropertyType Is GetType(Class1)).Single().Name
End Get
End Property
End Class
Public Class Class2
Public Property Mouse As String
Public Property Mickey As Class1
End Class
I think this is exactly what you're looking for. But if not, let me know and we can work it out.

Force multiple classes to have a specific shared member

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

Inheriting properties without inheritance

I've got a strange question here, and I suppose the answer is no, but... is there any way of inheriting a class' prooperties without inheriting it, just by composition?
What I got now is something like this:
Public Class Mixer
Inherits SomeOtherClass
Private _motor As Motor
Public Property Active() As Boolean
Get
Return _motor.Active
End Get
Set(ByVal value As Boolean)
_motor.Active = value
End Set
End Property
Public Property Frecuency() As Boolean
Get
Return _motor.Frecuency
End Get
Set(ByVal value As Boolean)
_motor.Frecuency = value
End Set
End Property
'More properties and functions from Mixer class, not from Motor
'
'
End Class
So I need the class Mixer to show publicly all it's Motor properties, but I don't want to inherit Motor since I it already inherits from SomeOtherClass. Is there any faster, cleaner and easier way of doing this?
Thanks!
Edit:
Just for clarifying: I know I could use an interface, but since the implementation of Motor is the same for all classes, I would like to inherit its properties directly, without having to implement them again in each class that has a Motor... but without inheriting Motor.
I believe you can use properties within an interface and then implement that interface.
Have a look at this question
You could always make your private _motor a public property then you'd be able to get to the Motor properties indirectly. I know it's not quite what you are asking for.
The most widely accepted solution (if not the only solution) is to extract a common interface that is implemented in every class that wraps an instance of Motor.
Public Interface IMotor
Property Active As Boolean
Property Frequency As Boolean
End Interface
Public Class Motor
Implements IMotor
Public Property Active As Boolean Implements IMotor.Active
Public Property Frequency As Boolean Implements IMotor.Frequency
End Class
Public Class Mixer
Inherits SomeOtherClass
Implements IMotor
Private _motor As Motor
Public Property Active() As Boolean Implements IMotor.Active
Get
Return _motor.Active
End Get
Set(ByVal value As Boolean)
_motor.Active = value
End Set
End Property
Public Property Frequency() As Boolean Implements IMotor.Frequency
Get
Return _motor.Frequency
End Get
Set(ByVal value As Boolean)
_motor.Frequency = value
End Set
End Property
End Class

How can I control the element names of serialized subclasses?

Let's say I have the following class structure (simplified from my real-world problem):
Public Class PC_People_Container
Private _people_list As New List(Of PL_Person)
Public Sub New()
End Sub
Public Sub Add(ByVal item As PL_Person)
_people_list.Add(item)
End Sub
Public Property PeopleList As List(Of PL_Person)
Get
Return _people_list
End Get
Set(ByVal value As List(Of PL_Person))
_people_list = value
End Set
End Property
End Class
Public Class PL_Person
Private _Name As String
Public Property Name As String
Get
Return _Name
End Get
Set(ByVal value As String)
_Name = value
End Set
End Property
Private _Contacts As ContactObject
Public Property Contacts As ContactObject
Get
Return _Contacts
End Get
Set(ByVal value As ContactObject)
_Contacts = value
End Set
End Property
Public Sub New()
End Sub
End Class
Public Class ContactObject
Public Property PhoneNumber As String
Public Property EmailAddress As String
Public Sub New()
End Sub
End Class
If I were to serialize this, I'd get the default assigned node names in my XML. That means my root is named PC_People_Container and each person in the list is marked up as PL_Person. I know I can change the root node using <XmlRoot(ElementName:="PeopleContainer")>. The trouble is doing that for the subclasses. I can't use the <XmlRoot> tag on PL_Person class because there can't be two root elements, and IntelliSense throws a fit when I try to use the <XmlElement> tag on a class like I would on a property. Is it even possible to control what those subclasses are named when they're serialized as child nodes?
PL_Person and ContactObject are not subclasses as you call them, they are merely property types.
This makes your question confusing because it suggests you may have a problem with inheritance (subclasses are classes that inherit from some base class) when in fact you just want your property elements to be named differently.
You should decorate your properties (not classes) with <XmlElement> to specify custom name:
<XmlElement("Persons", GetType(PL_Person))>
Public Property PeopleList As List(Of PL_Person)
As an afterthought, I would definitely not recommend calling your classes using such an awkward convention. In .NET, you should not use any prefixes or underscores in class names. Just call it Person.