Using interfaces as method parameters in C# or vb.net - vb.net

How to pass a object that is initialized from a derived class to a method that has Interface as the parameter? Below is the example of what I'm trying. Is it possible? Please suggest any better way of doing.
Public Interface IFruit
Property Name As String
Property Color As String
End Interface
Public Class Fruit
Implements IFruit
Private _Name As String
Private _Color As String
Public Property Color As String Implements IFruit.Color
Get
Return _Color
End Get
Set(value As String)
_Color = value
End Set
End Property
Public Property Name As String Implements IFruit.Name
Get
Return _Name
End Get
Set(value As String)
_Name = value
End Set
End Property
End Class
Public Class FruitExtended
Inherits Fruit
Private _Taste As String
Public Property Taste() As String
Get
Return _Taste
End Get
Set(ByVal value As String)
_Taste = value
End Set
End Property
End Class
Public Class A
Public Sub ProcessFruit(F as IFruit)
'...
'Do something
End Sub
will the below code work? or how to achieve this in other ways?
Public Sub Test()
Dim F1 as new FruitExtended()
ProcessFruit(F1)
End sub
End Class

I didn't try your code, but - in general - interfaces are good because they define a behaviuor rather than a state (properties). So maybe you could rethink your design and ask yourself what the ProcessFruit is supposed to do with a IFruit.
Some languages even disallow to declare properties in interfaces, other than constants. Java is an example.

Related

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.

Can I shorten the access of my Lazy class without disadvantage

So this is how I would design my Lazy class (From this SO):
Public NotInheritable Class MySingleton
Private Shared ReadOnly _instance As New Lazy(Of MySingleton)(Function() New _
MySingleton(), System.Threading.LazyThreadSafetyMode.ExecutionAndPublication)
Private Sub New()
End Sub
Public Shared ReadOnly Property Instance() As MySingleton
Get
Return _instance.Value
End Get
End Property
Private _MyString As String
Public Property MyString As String
Get
Return _MyString
End Get
Set(value As String)
_MyString = value
End Set
End Property
End Class
To access the _MyString value, I do the following:
Dim MyString = MySingleton.Instance.MyString
In fact, I always have to type the ".Instance."
Does it have any disadvantage if I design the Property the following way:
Public Property MyString As String
Get
Return instance._MyString
End Get
Set(value As String)
instance._MyString = value
End Set
End Property
So I can access it without always writing the ".Instance."
Dim MyString = MySingleton.MyString
Yes you can (of course with Public Shared Property), but you are losing some of the benefits from singleton over static classes.
Lets say you have another class MyWorker
Public Class MyWorker
Public Sub Work(instance as MySingleton)
Dim value as String = instance.MyString
' Do something ...
End Sub
End Class
I would not do this. This may not look like a big issue, but on the long run you have a tight coupling in your code base and a hard time mocking your class for unit testing, one of the reasons for using singeltons over static classes in the first place.
I often use this approach, when accessing Singelton values mutiple times:
Dim instance as MySingelton = MySingelton.Value
If instance.MyString = "something" Then
instance.MyString = "something else"
End If
much cleaner approach.

Need Help Initializing a Generic Property in VB.Net

I've created a request class. Here is an abbreviated version of it:
Public Class Request(Of T)
Private _Account As String
Public Property Account() As String
Get
Return _Account
End Get
Set(ByVal value As String)
_Account = value
End Set
End Property
Private _InnerRequest As T
Public Property InnerRequest() As T
Get
Return Me._InnerRequest
End Get
Set(ByVal value As T)
Me._InnerRequest = value
End Set
End Property
End Class
And then I have two other classes that I intend to use with this one - again, abbreviated
Public Class Individual
Public FirstName As String
Public LastName As String
Friend Sub New()
End Sub
End Class
And
Public Class Commercial
Public EntityName As String
Friend Sub New()
End Sub
End Class
Again, both of these are pretty abbreviated. The issue comes in when I attempt to use the properties of individual or commercial:
Dim Req As New Request(Of Individual)()
Req.InnerRequest.FirstName = "Herman" <-- Null Ref Exception
So... how do I get my inner request null ref exception kicked? I tried simply using Me._InnerRequest = New T in the New sub of Request, but no dice. Is there a way to handle this?
Req.InnerRequest must be set to an object instance of Individual first.
Req.InnerRequest = new Individual()
Req.InnerRequest.FirstName = "Herman"
Or create an instance for InnerRequest with the following modifications
Public Class Request(Of T As {New}) 'Classes of type T must have a public new constructor defined
::
Private _InnerRequest As New T() 'Creates a new class of type T when an instance is created of Request
And make the constructors of the other classes Public instead of Friend.
Than you can directly do
Dim Req As New Request(Of Individual)()
Req.InnerRequest.FirstName = "Herman"
#Barry already answered what the main problem is, but here's an alternate syntax if you prefer object initializers:
Req.InnerRequest = new Individual() With { FirstName = "Herman" }
Or, if you prefer, you could overload the constructor for your Individual class:
Dim individual As New Individual("Herman")
Req.InnerRequest = individual
With the Individual class looking like:
Public Class Individual
Public FirstName As String
Public LastName As String
Friend Sub New()
End Sub
Friend Sub New(firstName As String)
Me.FirstName = firstName
End Sub
End Class
You probably should consider restricting the T to some Entity class:
Public Class Request(Of T As Entity)
From which both Individual and Commercial will inherit:
Public Class Individual : Inherits Entity
Then maybe declare an overridable property Name of type String on this Entity class (which can be abstract/MustInherit), this should provide some flexibility. Otherwise you'd be having a hard time consuming your design pattern.

Best way to expose an object with read-only properties only

I can't find an answer to my question so I'm asking a new one.
I have an object where I want to fill it's properties from another class in the same solution. But the object should expose read-only properties only so the outside-caller can't see nor access the setter (cause there is no setter).
What is the best way to fill the internal backing variables from the same solution? I know I could do it in the constructor but I want to be able to set the variables after creating the object.
Sorry for my weird explaination, maybe a bit of code could help.
This is what I'm doing now:
Public Class ReadonlyObject
Protected Friend Sub New()
End Sub
'Could use this, but don't want to...
Protected Friend Sub New(foo As String)
End Sub
Friend _foo As String
Public ReadOnly Property Foo As String
Get
Return _foo
End Get
End Property
End Class
Public Class FillReadonlyObject
Private Sub DoSomeHeavyWork()
Dim roObject As New ReadonlyObject
roObject._foo = "bar"
'Could use this, but don't want to...want to access properties directly.
Dim roObject2 As New ReadonlyObject("bar")
End Sub
End Class
With this, the ReadonlyObject's properties are correctly exposed as readonly but I'm afraid it's bad practice.
I've seen implementations like this:
Public Class ReadonlyObject
Protected Friend Sub New()
End Sub
Private _foo As String
Public Property Foo As String
Get
Return _foo
End Get
Friend Set(value As String)
_foo = value
End Set
End Property
End Class
Public Class FillReadonlyObject
Private Sub DoSomeHeavyWork()
Dim roObject As New ReadonlyObject
roObject.Foo = "bar"
End Sub
End Class
This works, but exposes the property with a setter. It's not accessible, but it's visible and I don't want that :)
So maybe it's only a cosmetic thing but I think it's nice to tell the caller (or at least intellisense) the property is strictly read-only.
Thanks, Jan
If you want to explicitly declare the property as read-only, but then still have a way to set it after it is constructed, then all you need to do is create your own setter method rather than using the one automatically created for you but the property. For instance:
Public Class ReadonlyObject
Protected Friend Sub New()
End Sub
Private _foo As String
Public ReadOnly Property Foo As String
Get
Return _foo
End Get
End Property
Friend Sub SetFoo(value As String)
_foo = value
End Sub
End Class
Public Class FillReadonlyObject
Private Sub DoSomeHeavyWork()
Dim roObject As New ReadonlyObject
roObject.SetFoo("bar")
End Sub
End Class
Or, you could create two properties, like this:
Public Class ReadonlyObject
Protected Friend Sub New()
End Sub
Public ReadOnly Property Foo As String
Get
Return HiddenFoo
End Get
End Property
Friend Property HiddenFoo As String
End Class
Public Class FillReadonlyObject
Private Sub DoSomeHeavyWork()
Dim roObject As New ReadonlyObject
roObject.HiddenFoo = "bar"
End Sub
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.