I've seen some other responses about this and they talk about interfaces but I'm pretty sure you can do this with classes and base classes but I can't this to work.
Public Class Behavior
Private _name As String
Public ReadOnly Property Name As String
Get
Return _name
End Get
End Property
Public Property EditorUpdate As Boolean
Public Sub New(ByVal name As String)
_name = name
EditorUpdate = False
End Sub
Public Overridable Sub Update()
End Sub
' runs right away in editor mode. also runs when in stand alone game mode right away
Public Overridable Sub Start()
End Sub
' runs after game mode is done and right before back in editor mode
Public Overridable Sub Finish()
End Sub
' runs right when put into game mode
Public Overridable Sub Initialize()
End Sub
' runs when the game is complete in stand alone mode to clean up
Public Overridable Sub Destroy()
End Sub
End Class
Public Class CharacterController
Inherits Behavior.Behavior
Public Sub New()
MyBase.New("Character Controller")
End Sub
Public Overrides Sub Update()
' TODO: call UpdateController()
' THINK: how can UpdateController() get the controller entity it's attached to?
' Behaviors need a way to get the entity they are attached to. Have that set when it's assigned in the ctor?
End Sub
End Class
Dim plugins() As String
Dim asm As Assembly
plugins = Directory.GetFileSystemEntries(Path.Combine(Application.StartupPath, "Plugins"), "*.dll")
For i As Integer = 0 To plugins.Length - 1
asm = Assembly.LoadFrom(plugins(i))
For Each t As Type In asm.GetTypes
If t.IsPublic Then
If t.BaseType.Name = "Behavior" Then
behaviorTypes.Add(t.Name, t)
Dim b As Behavior.Behavior
b = CType(Activator.CreateInstance(t), Behavior.Behavior)
'Dim o As Object = Activator.CreateInstance(t)
End If
End If
Next
Next
When it tries to convert whatever Activator.CreateInstance(t) returns to the base class of type Behavior I'm getting invalid cast exception. That type should be of CharacterController which is defined as a child of Behavior so why wouldn't it let me cast that? I've done something like this before but I can't find my code. What am I missing?
This may not be an answer to your question (it also might resolve your exception -- who knows), but it is something that needs to be pointed out. These lines:
If t.IsPublic Then
If t.BaseType.Name = "Behavior" Then
Should really be changed to one conditional like this one:
If t.IsPublic AndAlso (Not t.IsAbstract) AndAlso _
GetType(Behavior.Behavior).IsAssignableFrom(t) Then
Otherwise, if somebody defines a random type called "Behavior" in their own assembly and derives it from another type, your code will think it is a plugin. Additionally, if someone derives your Behavior type and then derives that type (two levels of inheritance) this code will incorrectly skip over that type. Using the IsAssignableFrom method is a quick and easy way to ensure that one type does actually derive from the specific type you want (instead of any type that shares the same name), even if there is another type in between your types in the inheritance tree. The additional check against t.IsAbstract will also ensure that you don't try to instantiate an abstract subtype of your base plugin type.
This works for me:
Dim ctor As Reflection.ConstructorInfo = _
t.GetConstructor(New System.Type() {})
Dim o As Object = ctor.Invoke(New Object() {})
Dim plugin As Plugin = TryCast(o, Plugin)
(If I find t, I invoke the parameterless constructor.)
[I just realized this is probably what Activator.CreateInstance does, so I replaced my code with yours and it worked your way -- so this probably won't help you]
Related
In VB, I have a class that does some standard validations. What I'd LIKE to do is to declare some variables, then create instances of a validator class that include pointers to the variables, and then at some later time execute the validators to test the values in the fields that are pointed to.
Something like this:
public class MyData
public property foo as string
public property bar as string
dim vfoo as validator
dim vbar as validator
public sub new()
vfoo=new validator(&foo) ' i.e. & operator like in C
vbar=new validator(&bar)
end sub
public sub validate()
vfoo.validate
vbar.validate
end sub
end class
public class validator
dim _field as string* ' i.e. * like in C
public sub new(field as string*)
_field=field
end sub
public sub validate
if string.isnullorempty(_field) then
throw SomeException
else if not SomeOtherTest(_field) then
throw SomeOtherException
end sub
The catch is that, to the best of my knowledge, there is nothing like C pointers in VB. Is there any reasonably easy way to do this?
At present I am passing in the field values at the time I call the validate() function, but this is not ideal because I would like to be able to create a List of validators specific to a given caller, and then loop through the List. But at the time I loop, how would I know which value from MyClass to pass in, unless I had a giant select statement keying off some "field code"? (And of course in real life, there are not just two fields like in this example, there are quite a few.)
Am I just having a brain freeze and there's an easy way to do this? Or can this not be done in VB because there are no such thing as pointers?
Like Java, VB doesn't make direct use of pointers (it compensates where it can with library/framework calls). In the context of a garbage-collected language, I can't imagine that this style of validation would work out well.
But for fun, maybe a lambda-based solution could suit?:
Public Class MyData
Public Property foo As String
Public Property bar As String
Dim vfoo As validator
Dim vbar As validator
Public Sub New()
vfoo = New validator(Function() foo)
vbar = New validator(Function() bar)
End Sub
Public Sub validate()
vfoo.validate()
vbar.validate()
End Sub
End Class
Public Class validator
ReadOnly _fieldFunc As Func(Of String)
Public Sub New(fieldFunc As Func(Of String))
_fieldFunc = fieldFunc
End Sub
Public Sub validate()
Dim _field = _fieldFunc()
If String.IsNullOrEmpty(_field) Then
Throw New Exception("NullOrEmpty")
ElseIf Not SomeOtherTest(_field) Then
Throw New Exception("SomeOtherTest")
End If
End Sub
Public Function SomeOtherTest(f As String) As Boolean
Return True
End Function
End Class
I'm looking for a way to get the value of a property during or after the New() sub. In general though I would like to know if there is a way to call some code automatically after a class has all of its properties fully initiated.
During the Sub New() properties are set to their initial values, rather than the ones that are set up at design time.
Essentially I'm wondering if it's possible to setup something similar to the "Form Shown" event, except for classes.
Code:
Public Class Test
inherits Component
Public Event Initiated()
Public Sub New()
MyBase.New()
RaiseEvent Initiated()
End Sub
Private Sub OnInitiated() Handles Me.Initiated
Debug.WriteLine(Max)
End Sub
Private _Max As Integer = 5
Public Property Max() As Integer
Get
Return _Max
End Get
Set(ByVal Value As Integer)
_Max = Value
End Set
End Property
End Class
Note: The value of the "Max" property is set to 3 in the design view.
The issue with using the constructor is that the designer code sets your properties well after it creates your object. But, the NET Framework includes the interface ISupportInitialize which is ideal for controls and components which need to do things such as qualify properties conditionally - for instance checking Value after Min and Max are set.
It is easy to use:
Imports System.ComponentModel
Public Class Test
Inherits Component
Implements ISupportInitialize
When you press enter on the last line, it will add 2 methods:
Public Sub BeginInit() Implements ISupportInitialize.BeginInit
Public Sub EndInit() Implements ISupportInitialize.EndInit
Which allows you to do this:
Public Sub New()
MyBase.New()
End Sub
Public Sub EndInit() Implements ISupportInitialize.EndInit
' do whatever you want to do
' all properties will be initialized at this time
' e.g. Max will be the IDE value, not 5
...
End Sub
The way it works is that VS will invoke this from the designer code after the control/component properties. If you open the designer code you will see something like this:
' ctl declarations
CType(Me.Test1, System.ComponentModel.ISupportInitialize).BeginInit()
' lots of code initializing controls
Me.Label1.Name = "Label1"
...
Me.Button1.Location = ...
...
Me.Test1.Max = 3 ' yours will be there somewhere
' then at the end:
CType(Me.Test1, System.ComponentModel.ISupportInitialize).EndInit()
So, you can add any code you need to run before anything is created in your BeginInit method, and code you need to run after all properties are initialized in EndInit.
BeginInit and EndInit will run every time the designer code is run. That is, every time at runtime and after there are enough changes to the form that it needs to be rebuilt. You do need to keep your component code fresh since VS is using a compiled version of it in the IDE when working on the project using it.
So, Rebuild often and Clean when it seems like it is not picking up changes.
The only thing I could see to answer your question is to set up a custom event in your class and fire it at the end of the constructor
I'm completely stuck in a situation and I have no idea on where to go from here. I'm creating a very large project, so my goal is to keep the code itself as clean as possible and keeping as many hacks as possible out of the mix.
Here is the situation.
I have a class called Woo_Type, it is the parent of my many derived classes.
Public MustInherit Class Woo_Type
Private Shared TypeList As New Dictionary(Of String, Woo_Type)
Public MustOverride Sub SetValue(ByVal val As Object)
Public MustOverride Function GetValue() As Object
Public Shared Function GetTypeFromName(ByVal name As String) As Woo_Type
Return TypeList(name)
End Function
Public Shared Sub AddType(ByVal name As String, ByVal def As Woo_Type)
TypeList.Add(name, def)
End Sub
End Class
I have many classes that Inherit from Woo_Type that have similar structures to this:
Public Class Woo_tpInt
Inherits Woo_Type
Private value As Integer = 0
Public Overrides Function GetValue() As Object
Return value
End Function
Public Overrides Sub SetValue(val As Object)
value = val
End Sub
End Class
I want to be able to do things like:
Woo_Type.GetTypeFromName("int")
And have it return something like the class or something...
At this point I'm really confused as to what I want and I didn't know if anybody had any suggestions. To make sure that GetTypeFromName worked correctly, I had in an Initializer sub the following:
Public Sub InitializeTypes()
Woo_Type.AddType("int", Woo_tpInt)
Woo_Type.AddType("String", Woo_tpInt)
End Sub
But I quickly realized that-that obviously doesn't work either.
So this may seem confusing but I'm basically wondering how to better structure this so that everything works...
What do you want to do with the result? Are you sure you don't simply need generics?
Public Class WooType(Of T)
Public Property Value As T
End Class
Public Class Test
Public Sub Foo()
Dim int As New WooType(Of Integer)
int.Value = 42
Dim str As New WooType(Of String)
str.Value = "Forty-Two"
End Sub
End Class
If what you want to do is get the type itself (as opposed to an object), I would recommend using reflection rather than trying to reinvent the wheel. For instance, to get the Woo_tpInt type, you could do this:
Dim a As Assembly = Assembly.GetExecutingAssembly()
Dim t As Type = a.GetType("WindowsApplication1.Woo_tpInt") ' Change WindowsApplication1 to whatever your namespace is
If you want to use a shorter name like "int" to mean "WindowsApplication1.Woo_tpInt", you could create a dictionary to store the translation table, for instance:
Dim typeNames As New Dictionary(Of String, String)
typeNames.Add("int", GetType(Woo_tpInt).FullName)
Dim a As Assembly = Assembly.GetExecutingAssembly()
Dim t As Type = a.GetType(typeNames("int"))
I'm doing some LINQ which requires a custom comparer, so I created a new class implementing IEqualityComparer. However, when I use it, I have to create an instance of it each time.
Dim oldListOnly = oldList.Except(newList, New MyEqualityComparer)
Dim newListOnly = newList.Except(oldList, New MyEqualityComparer)
I may be misunderstanding how .NET works, but it seems wasteful to create a new comparer each time. I really just want one instance (the equivalent of static in C++/C#).
So I tried creating a "static" class, which in vb.net is a module. But got an 'Implements' not valid in Modules error.
I then tried making the Equals and GetHashCode function shared methods on my class, but got this error: Methods that implement interface members cannot be declared 'Shared'.
Any ideas how to accomplish my goal here? Or am I simply misunderstanding what's going behind the scenes?
Your understanding is correct, although the waste is unlikely to be noticeable. For your situation, you could use the singleton pattern, which usually goes something like this:
Public Class MyEqualityComparer
Implements IEqualityComparer(Of whatever)
Private Sub New()
'no outsider creation
End Sub
Private Shared ReadOnly _instance As New MyEqualityComparer()
Public Shared ReadOnly Property Instance As MyEqualityComparer
Get
Return _instance
End Get
End Property
'other code
End Class
Why not simply do
Dim comparer = New MyEqualityComparer
Dim oldListOnly = oldList.Except(newList, comparer )
Dim newListOnly = newList.Except(oldList, comparer )
There needs to be an instance of a concrete type that implements IEqualityComparer. What you can do with a module, however, is define a public instance which is initialized to "New EqualityComparer". You can then pass that default instance to the Except method.
Something like:
Public Module MyComparer
Public acmeComparer As acmeCompareType
Public Class acmeCompareType
Implements IEqualityComparer(Of System.Drawing.Point)
Public Function Equals1(x As System.Drawing.Point, y As System.Drawing.Point) As Boolean Implements System.Collections.Generic.IEqualityComparer(Of System.Drawing.Point).Equals
Return Math.Abs(x.X) = Math.Abs(y.X) AndAlso Math.Abs(x.Y) = Math.Abs(y.Y)
End Function
Public Function GetHashCode1(obj As System.Drawing.Point) As Integer Implements System.Collections.Generic.IEqualityComparer(Of System.Drawing.Point).GetHashCode
' Note that obj is a struct passed by value, so we can safely modify it here
' (without affecting the caller's instance)
obj.X = Math.Abs(obj.X)
obj.Y = Math.Abs(obj.Y)
Return obj.GetHashCode
End Function
End Class
End Module
I have a BaseClass, a DerivedClass1 and a DerivedClass2 from a third party library. DerivedClass1 and DerivedClass2 both inherit from BaseClass.
There's a ContainerClass, from the same library, with a member variable ActiveItem, which can be of DerivedClass1 or DerivedClass2, so it is declared as BaseClass.
I want to know if ActiveItem is of DerivedClass1, as it can change in runtime without notice.
If I do
Dim isDerivedClass1 as boolean = TypeOf(oject.ActiveItem) Is DerivedClass1
then I get a compile time error, telling me that ActiveItem can never be of DerivedClass1 type.
I have tried several combinations of GetType and TypeOf but it doesn't seem possible to check this. I have also tried to declare an auxiliary DerivedClass1 variable and comparing their types, but haven't got any luck either.
Is there any workaround?
I guess I could do it with Reflection, but seems really overkill.
Edit:
The following code doesn't compile in vs2005 SP1.
Public Class Base
Public x As Integer
End Class
Public Class Derived1
Inherits Base
Public y As Integer
End Class
Public Class Derived2
Inherits Base
Public z As Integer
End Class
Public Class Unrelated
Public var As Base
End Class
Public Class Form1
Public Sub Test(ByVal obj As Unrelated)
Dim tst As Boolean
tst = TypeOf obj Is Derived1
End Sub
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim obj As New Unrelated
obj.var = New Derived1
Test(obj)
End Sub
End Class
Edit:
It seems that the original problem was a fault in my side. I was checking against the wrong type (those silly third part libraries...)
However, I'm still trying to find the error in the code above.
Edit:
Again, my fault. I'm checking the Unrelated type against Base.
You code seems to be almost exactly right.
I've done this, which works fine:
Dim isDerivedClass1 As Boolean = TypeOf oject.ActiveItem Is DerivedClass1
Dim isDerivedClass2 As Boolean = TypeOf oject.ActiveItem Is DerivedClass2
Have I missed something?
EDIT: I think you just missed the var property in your edited code.
Public Sub Test(ByVal obj As Unrelated)
Dim tst As Boolean
tst = TypeOf obj.var Is Derived1
End Sub
You'll have to trust the compiler on this, it is convinced that DerivedClass1 does not inherit BaseClass. It doesn't get that wrong. That's either because it didn't see the Inherits clause in the DerivedClass1 declaration or because it picked a BaseClass definition from another assembly.
To fix the former problem, you have no alternative but to declare the ActiveItem as Object or to find another type that these classes have in common. Use the Object Browser. To fix the latter problem you'll have to change the Imports directive or spell out the full name of the BaseClass type (including namespace).
A simple possibility could be to use TryCast:
Dim isDerivedClass1 As Boolean = TryCast(object.ActiveItem, DerivedClass1) IsNot Nothing