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
I am trying to use Interception Call Handler and Handler Attributes on my interfaces (or implementation). Lets say my Interface has two methods DoSomething() and DoSomethingElse(), and I only have the interceptor for DoSomethingElse(); when I resolve my main interface the constructor for the Call Handler get called even if I never invoke DoSomethingElse().
I tried this by resolving to Lazy(of IMainInterface) but the moment I call the function DoSomething() the Call Handler is still created unnecessarily. Is there a way to prevent this from happening either by code or configuration. Here is my sample implementation
Handler Attribute and Call Handler
<AttributeUsage(AttributeTargets.Method)>
Public Class NormalSampleAttribute
Inherits HandlerAttribute
Public Sub New()
Console.WriteLine("Constructor of Normal Sample Attribute")
End Sub
Public Overrides Function CreateHandler(container As Microsoft.Practices.Unity.IUnityContainer) As Microsoft.Practices.Unity.InterceptionExtension.ICallHandler
Console.WriteLine("Create Handler")
Return New SampleCallHandler
End Function
End Class
Public Class SampleCallHandler
Implements ICallHandler
Public Sub New()
Console.WriteLine("Constructor of Sample Call handler")
End Sub
Public Function Invoke(input As IMethodInvocation, getNext As GetNextHandlerDelegate) As IMethodReturn Implements ICallHandler.Invoke
Console.WriteLine("Invoke of Sample Call handler - " & input.MethodBase.Name)
Return getNext.Invoke(input, getNext)
End Function
Public Property Order As Integer Implements ICallHandler.Order
End Class
Interface and implementation
Public Interface IMainInterface
Sub DoSomething()
<NormalSample()>
Sub DoSomethingElse()
End Interface
Public Class MainClass
Implements IMainInterface
Public Sub New()
Console.WriteLine("main class - Constructor")
End Sub
Public Sub DoSomething() Implements IMainInterface.DoSomething
Console.WriteLine("main class do something...")
End Sub
Public Sub DoSomethingElse() Implements IMainInterface.DoSomethingElse
Console.WriteLine("main class do something else...")
End Sub
End Class
Main module to register and execute the method
Module Module1
Public container As IUnityContainer
Sub Main()
container = New UnityContainer
DoRegistrations()
Console.WriteLine("Before lazy Resolve")
Dim lmc As Lazy(Of IMainInterface) = container.Resolve(Of Lazy(Of IMainInterface))()
Console.WriteLine("Before making lazy function call")
lmc.Value.DoSomething()
Console.ReadLine()
End Sub
Sub DoRegistrations()
container.AddNewExtension(Of InterceptionExtension.Interception)()
container.RegisterType(Of IMainInterface, MainClass)()
container.Configure(Of Interception).SetDefaultInterceptorFor(Of IMainInterface)(New InterfaceInterceptor)
End Sub
End Module
It produces the following output:
Before lazy Resolve
Before making lazy function call
main class - Constructor
Constructor of Normal Sample Attribute
Create Handler
Constructor of Sample Call handler
main class do something...
Even though DoSomethingElse() is never called the cost of the handler creation is being added on all flows. Is there any way to avoid this? Any help is appreciated.
Thanks in advance!
SV
I would like to write a nested class into an existing class of my own. But I can't find how because I have no idea how this is really called.
What do I mean by nested class? With a table dt from the DataTable class, I can write dt.Columns.add(). Columns would be property of the main class and add would be a method from a nested class.
Any suggestions?
That is not a nested class, it's simply a class. The Columns property is of the type DataColumnCollection that has a public method called Add. To build your own in a similar fashion it would simply be:
Public Class MyFirstClass
Public Sub New()
End Sub
Dim _second As New MySecondClass()
Public Property Second() As MySecondClass
Get
Return _second
End Get
Set(ByVal Value As MySecondClass)
_second = Value
End Set
End Property
End Class
Public Class MySecondClass
Public Sub New()
End Sub
Public Sub MySecondClassMethod()
'Do something
End Sub
End Class
This would then be called in some other class or functionality like:
Dim x as New MyFirstClass()
x.Second.MySecondClassMethod()
I am getting an error when trying to call a form from within a method:
Expression is not a method
My code structure looks like this:
Public Class frmMain
Class Server
Private Shared Sub StringMessageReceived()
Call frmMM()
End Sub
End Class
End Class
How can I call the windows form within the class?
You create an instance of the form, and then call its Show (or ShowDialog) method:
Public Class frmMain
Class Server
Private Shared Sub StringMessageReceived()
Call New frmMM().Show()
End Sub
End Class
End Class
Consider the following example:
Public Class ParentClass
Public Sub GenerateReport
Dim Col As Collection
Col = GetItemCollection()
End Sub
Public Overridable Function GetItemCollection() As Collection
GetItemCollection = New Collection
GetItemCollection.Add("1")
GetItemCollection.Add("2")
GetItemCollection.Add("3")
End Function
End Class
Public Class ExtendedClass
Inherits ParentClass
Public Overrides Function GetItemCollection() As Collection
GetItemCollection = New Collection
GetItemCollection.Add("A")
GetItemCollection.Add("B")
GetItemCollection.Add("C")
End Function
End Class
Public Sub Main()
Dim cls As New ExtendedClass
cls.GenerateReport()
End Sub
When Main() calls cls.GenerateReport(), is the variable Col going to be a collection of numbers or letters? I'm hoping that it will recognize that cls is an instance of ExtendedClass and call the overridden method and return the letters.
It will be a collection of letters as you did override the method. However, where did you declare the GetItemCollection? You still need an instance variable.