Can I shorten the access of my Lazy class without disadvantage - vb.net

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.

Related

Get a Field Object, not FieldInfo, from a VB Class Instance

I am trying to iterate through objects (fields) in a class and invoke a method on each object. Each object is of a different type. Here is the parent class:
Public Class MySettings
Public IdentifyByFacType As RadioButtonSetting
Public WtrFacTypes As ListSetting
Public OilFacTypes As ListSetting
Public GroupByRef As CheckboxSetting
Public GroupRefAttr As TxtboxSetting
End Class
Here is part of one of the sub-object classes:
<Serializable>
Public Class TxtboxSetting
<XmlIgnore()>
Public MyControl As Windows.Forms.TextBox
<XmlIgnore()>
Public DefaultSetting As String
Private _SavedSetting As String
Public Property SavedSetting As String
Get
Return _SavedSetting
End Get
Set(value As String)
_SavedSetting = value
CurrentValue = value
End Set
End Property
Public Sub New()
End Sub
Public Sub New(DefaultSetting As String, MyControl As Windows.Forms.TextBox)
Me.DefaultSetting = DefaultSetting
Me.MyControl = MyControl
End Sub
Public Sub RestoreDefault()
CurrentValue = DefaultSetting
End Sub
End Class
All of the sub-objects of the MySettings class, like GroupRefAttr for example, have the same methods and properties, but the internal code is different.
So I will have several classes like the MySettings class, and each one will have different sub-objects. Given an instance of such a class, I want to automatically iterate through the fields and call a method RestoreDefault on each one. I don't want to have to know what objects exist in the MySettings class. Rather, knowing that they all have the RestoreDefaultmethod, I want simply call the method on each object.
Despite much searching, I have not found a way to do this. With reflection, I can only get this far:
Dim Opts as New MySettings
For Each var In Opts.GetType.GetFields
Dim RestoreDefault As System.Reflection.MethodInfo = var.FieldType.GetMethod("RestoreDefault")
RestoreDefault.Invoke(Opts, Nothing)
Next
However, in the line RestoreDefault.Invoke(Opts, Nothing), I can't just pass in Opts, as I am dealing with a field in Opts, not Opts itself. A statement like this would work: RestoreDefault.Invoke(Opts.GroupRefAttr, Nothing), but that requires me to know the objects in the MySettings class ahead of time, and that defeats the purpose. Is there a way to grab field instance objects at runtime and pull this off?
When you invoke the RestoreDefault method you need to invoke it on the setting (i.e., the value of the field), not the class containing the setting. Changing your code to this should fix your problem:
Dim Opts as New MySettings
For Each var In Opts.GetType.GetFields
Dim setting As Object = var.GetValue(Opts)
Dim RestoreDefault As System.Reflection.MethodInfo = var.FieldType.GetMethod("RestoreDefault")
RestoreDefault.Invoke(setting, Nothing)
Next
However, if you introduce either a base class or an interface you should be able to get rid of some or all of the reflection. The container setting class can have a collection of settings that each have a shared base class or interface with a RestoreDefault method. The container setting class will then call this method through the base class or interface without having to use reflection.
The base class:
Public MustInherit Class BaseSetting
Public MustOverride Sub RestoreDefault
End Class
A specific settings class:
Public Class TxtboxSetting
Inherits BaseSetting
Public Overrides Sub RestoreDefault()
' Specific implementation
End Sub
End Class
On any class deriving from BaseSetting you can now call the RestoreDefault method without having to use reflection.
However, considering your design you might still want to use reflection to get the settings containd in the MySettings class. You can do it like this:
Dim settings = From fieldInfo in Opts.GetType.GetFields
Where GetType(BaseSetting).IsAssignableFrom(fieldInfo.FieldType)
Select DirectCast(fieldInfo.GetValue(Opts), BaseSetting)
For Each setting In settings
setting.RestoreDefault()
Next
Reflection is used to find all the fields deriving from BaseSetting and then RestoreDefault is called on each field. This method does not suffer from the "magic string" problem where your code depends on the name of the RestoreDefault method represented in a string.
(Also, calling the MySettings class the parent is a bit misleading because there is nothing inheriting from MySettings. Instead this class contains other settings.)
All of the sub-objects of the MySettings class, like GroupRefAttr for example, have the same methods and properties, but the internal code is different.
In that case, the sub-object types should be defined such that they implement a common interface that demands these same methods and properties exist. For now, I'll name that interface IControlSetting. Then, your For loop looks something like this:
Dim Opts as New MySettings
For Each var In Opts.GetType.GetFields
Dim setting As IControlSetting = TryCast(var.GetValue(Opts), IControlSetting)
If setting Is Nothing Then Continue
setting.RestoreDefault()
Next
Additionally, I'd change your MySettings type to encapsulate a dictionary or IControlSetting objects. Then you can just iterate the dictionary to check each of the objects, rather than needing reflection. That might look like this:
Public Class MySettings
Private allSettings As Dictionary(Of String, IControlSetting)
Public Sub New()
allSettings = new Dictionary(Of String, IControlSetting)()
allSettings.Add("IdentifyByFacType", New RadioButtonSetting())
allSettings.Add("WtrFacTypes", New ListSetting())
allSettings.Add("OilFacTypes", New ListSetting())
'...
End Sub
Public Property IdentifyByFacType As RadioButtonSetting
Get
Return DirectCast(allSettings("IdentifyByFacType"), RadioButtonSetting)
End Get
'The setters may be optional, depending on how you expect to use these
Set(ByVal value As RadioButtonSetting)
allSettings("IdentifyByFacType") = value
End Set
End Property
Public Property WtrFacTypes As ListSetting
Get
Return DirectCast(allSettings("WtrFacTypes"), RadioButtonSetting)
End Get
Set(ByVal value As ListSetting)
allSettings("WtrFacTypes") = value
End Set
End Property
Public Property OilFacTypes As ListSetting
Get
Return DirectCast(allSettings("OilFacTypes"), RadioButtonSetting)
End Get
Set(ByVal value As ListSetting)
allSettings("OilFacTypes") = value
End Set
End Property
'...
Public Sub RestoreAllDefaults()
For Each setting As KeyValuePair(Of String, IControlSetting) In allSettings
setting.Value.RestoreDefault()
Next setting
End Sub
End Class

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

Using interfaces as method parameters in C# or 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.

Can't create a Collection of Inherited Classes

Maybe I just don't know what to search for, but I'm going a little bonkers here trying to figure out how to create a collection of inherited classes. The base class, I will never use.
Basically, I have 3 components:
A base class call ImageFormat
Child classes of ImageForm
Code in Sub Main() to loop create a
collection and loop through it.
So it does it, #3. The problem is that it always gets the last item added to the collection and uses it's values only.
Here's my Base Class:
Public MustInherit Class ImageFormat
Protected Shared _extentions As String()
Protected Shared _targettype As String
Protected Shared _name As String
Public ReadOnly Property Extentions As String()
Get
Return _extentions
End Get
End Property
Public ReadOnly Property TargetType As String
Get
Return _targettype
End Get
End Property
Public ReadOnly Property Name As String
Get
Return _name
End Get
End Property
End Class
And here are the child classes:
Class WindowsEnhancedMetafile
Inherits ImageFormat
Sub New()
_extentions = {"EMF"}
_targettype = "jpg"
_name = "Windows Enhanced Metafile"
End Sub
End Class
Class WindowsBitmap
Inherits ImageFormat
Sub New()
_extentions = {"BMP", "DIB", "RLE", "BMZ"}
_targettype = "jpg"
_name = "Windows Bitmap"
End Sub
End Class
Class WindowsMetafile
Inherits ImageFormat
Sub New()
_extentions = {"WMF"}
_targettype = "jpg"
_name = "Windows Metafile"
End Sub
End Class
(don't know if these child classes need to different, like just instantied from ImageFormat type or Singleton patterns - would appreciate anything thoughts you have on this)
Then, my routine is:
Sub Main()
Dim imgFormats As New List(Of ImageFormat)
imgFormats.Add(New WindowsBitmap)
imgFormats.Add(New WindowsMetafile)
imgFormats.Add(New WindowsEnhancedMetafile)
Dim name As String = String.Empty
For Each imgFormat In imgFormats
name = imgFormat.Name
Console.WriteLine(name)
Next
Console.ReadLine()
End Sub
This returns Windows Enhanced Metafile three times at the Console. What am I doing wrong here?
The three properties:
Protected Shared _extentions As String()
Protected Shared _targettype As String
Protected Shared _name As String
Are marked as Shared - they belong to the class not the object.
Each time you assign a new value to _name it overrides the old value, thus why you get the same name printed each time.
It should be:
Protected _extentions As String()
Protected _targettype As String
Protected _name As String
Well, your _name et al are Shared, which means they are class-level variables. When you're adding WindowsEnhancedMetafile, it happens to overwrite these fields with WMF-specific information. If you changed your code to:
imgFormats.Add(New WindowsMetafile)
imgFormats.Add(New WindowsEnhancedMetafile)
imgFormats.Add(New WindowsBitmap)
you would've had "Windows Bitmap" printed three times.
All you have to do is to change your field declarations to
Protected _extentions As String()
Protected _targettype As String
Protected _name As String