Using Reflection to Get All Static Properties in a Class As Objects VB.NET - vb.net

I would like to start that I don't want a to hear about how expensive and terrible reflection is. That won't help—I have a very good reason to use reflection and that's not my question.
Specifically, I have a class within a class that contains several static properties of the same type.
Public Class Foo
Public Class Bar
Public Shared Property prop1 As New CustomClass()
Public Shared Property prop2 As New CustomClass()
Public Shared Property prop3 As New CustomClass()
End Class
End Class
Public Class CustomClass
Public Sub DoStuff()
End Sub
End Class
I'm looking to create a method in Foo that calls DoStuff on each of the properties contained within it. How can I do this? Here's the general idea of what I want to include in Foo, but I obviously can't convert PropertyInfo to CustomClass:
Private Sub Example()
For Each prop As PropertyInfo In GetType(Foo.Bar).GetProperties()
DirectCast(prop, CustomClass).DoStuff()
Next
End Sub
How can I get the static properties and cast them to CustomClass objects?

PropertyInfo represents the type's property get/set method pair. To evaluate the getter you simply call GetValue, like so:
(in C# because I'm a language snob)
foreach( PropertyInfo pi in typeof(Foo.Bar).GetProperties() ) {
// Use null as arguments because it's a static property without an indexer.
Object got = pi.GetValue( null, null );
CustomClass got2 = got as CustomClass;
if( got2 != null ) {
Console.WriteLine( got2.ToString() );
}
}

And to convert Dai's answer to VB because I'm not a language snob:
For Each pi As System.Reflection.PropertyInfo in Foo.Bar.GetType.GetProperties()
' Use nothing as arguments because it's a shared property without an indexer.
Dim got = pi.GetValue(Nothing, Nothing)
Dim got2 as CustomClass = DirectCast(got, CustomClass)
If Not IsNothing(got2) Then Console.WriteLine(got2.toString())
Next
huzzah for less lines and more keystrokes...

Related

Shorthand Notation on an Overrideable Property in VB.Net Not Working

In my VB.Net code, I know that when I declare a property in a class I can do so using the shorthand of, for example, Public Property Prop1 As String and .Net automatically creates a private variable _Prop1 that I can refer to within my class.
I also know that I can refer to that variable within my code using either _Prop1 or by Prop1.
Now since I've always assumed that the preferred method is to use the private variable, I've always tried to make modifications / write code within my class referring to _Prop1. This is where my problem now comes in...
I have the following situation:
Public Class MyClass_Base
Public Overridable Property Prop1 As String = "val1"
Public Sub Test()
If _Prop1 = ....
End Sub
End Class
Public Class MyClass
Inherits MyClass_Base
Public Overrides Property Prop1 As String = "val2"
End Class
Basically, I define the property as Overridable in my base class, now when I get to the Test() subroutine, _Prop1 has the value of Nothing. However Prop1 has the correct value of val2.
Programming gurus out there, what is the correct way to deal with this situation?
Do I ignore the auto-created prive variable _Prop1 and work with the public Prop1 or is there something else I should add in / not even use the shorthand notation for this and write my own getter / setter logic to ensure things change as I would like?
Thanks!!!
I think you've answered your own question. You should absolutely not rely on hidden compiler mechanics in your own code. You're referencing a variable that you did not declare and there is no guarantee in the framework that this variable will be there (or work as you might expect, as you've discovered) - it's a hack to use it, so don't.
Unless you have a very good reason not to, code in the class should reference the public property just the same as code using the class would. If you're using automatic properties then there is no difference between doing that and using the private variable. It also has the benefit that if you do, at a later time, decide to implement explicit getters and setters that your code does not break and that your new getters and setters get called.
To explore the reason you get the unexpected result, in declaring
Public Overrides Property Prop1 As String = "val2"
You end up with two different _Prop1 variables - MyClass_Base._Prop1 and MyClass._Prop1. When you then call Test(), you are calling the base class method and that will refer to the base class's _Prop1 variable which has not been set to anything since you have overriden the implicit variable, getter, and setter in the subclass.
To illustrate the point, this is similar to :
Public Class MyClass_Base
Private _Prop1 As String = "val1"
Public Sub Test()
Console.WriteLine(_Prop1)
End Sub
End Class
Public Class MySubClass
Inherits MyClass_Base
Private _Prop1 As String = "val2"
End Class
Sub Main()
Dim class1 As New MyClass_Base
Dim class2 As New MySubClass
class1.Test()
class2.Test()
End Sub
Where your output will be :
val1
val1
In the above case MyClass_Base._Prop1 is always initialized, however, while in your case, it is not. In either case, the Test() method belongs to the base class so it will always refer to its own _Prop1 and not any variables of the same name declared in subclasses.
If you do need to refer to the private field, for whatever reason, you have to be very careful about how you do it (and the implications that follow). Any method that does so would need to itself be Overridable if subclasses are intended to work with their own private variables in the same way. Not to suggest that you should continue to use implicit variables, but to demonstrate :
Public Class MyClass_Base
Public Overridable Property Prop1 As String = "val1"
Public Overridable Sub Test()
Console.WriteLine(_Prop1)
End Sub
End Class
Public Class MySubClass
Inherits MyClass_Base
Public Overrides Property Prop1 As String = "val2"
Public Overrides Sub Test()
Console.WriteLine(_Prop1)
End Sub
End Class
Here we get the "expected" result because MySubClass overrides test to reference its own private field. Better to just stick to using the property names.
From the documentation on Auto Implemented Properties:
"Attributes specified for the property do not apply to the backing field."
This is one of those areas where C# and VB.NET need to be aligned.
C# (correctly) does not allow you to access the auto implemented property backing field (without some convoluted work). I honestly don't know what you can access this in VB.
So the rule here is even though you can access the backing field of an auto implemented property you really shouldn't modify this directly (nor should you need to)
If you need to then you should use a defined backing field (with initialisation) and explicit Get and Set

VB typeof operator with generics

When comparing types in VB the following works as expected and enables the current instance to be compared against a specific inherited class, in this case returning False (snippet from LINQPad)
Sub Main
Dim a As New MyOtherChildClass
a.IsType().Dump()
End Sub
' Define other methods and classes here
MustInherit class MyBaseClass
Public Function IsType() As Boolean
Return TypeOf Me Is MyChildClass
End Function
End Class
Class MyChildClass
Inherits MyBaseClass
End Class
Class MyOtherChildClass
Inherits MyBaseClass
End Class
However when generics are introduced the VB compiler fails with the error Expression of type 'UserQuery.MyBaseClass(Of T)' can never be of type 'UserQuery.MyChildClass'.
' Define other methods and classes here
MustInherit class MyBaseClass(Of T)
Public Function IsType() As Boolean
Return TypeOf Me Is MyChildClass
End Function
End Class
Class MyChildClass
Inherits MyBaseClass(Of String)
End Class
Class MyOtherChildClass
Inherits MyBaseClass(Of String)
End Class
The equivalent code in C# compiles and allows the comparison, returning the correct result
void Main()
{
var a = new MyOtherChildClass();
a.IsType().Dump();
}
// Define other methods and classes here
abstract class MyBaseClass<T>
{
public bool IsType()
{
return this is MyChildClass;
}
}
class MyChildClass : MyBaseClass<string>
{
}
class MyOtherChildClass : MyBaseClass<string>
{
}
Why does the VB compiler not allow this comparison?
You raise an interesting point about VB/C# compilation that I can't really speak to. If you're looking for a solution, here's a way to do it from the question How can I recognize a generic class?
Define these functions:
Public Function IsSubclassOf(ByVal childType As Type, ByVal parentType As Type) As Boolean
Dim isParentGeneric As Boolean = parentType.IsGenericType
Return IsSubclassOf(childType, parentType, isParentGeneric)
End Function
Private Function IsSubclassOf(ByVal childType As Type, ByVal parentType As Type, ByVal isParentGeneric As Boolean) As Boolean
If childType Is Nothing Then
Return False
End If
If isParentGeneric AndAlso childType.IsGenericType Then
childType = childType.GetGenericTypeDefinition()
End If
If childType Is parentType Then
Return True
End If
Return IsSubclassOf(childType.BaseType, parentType, isParentGeneric)
End Function
Call like this:
Dim baseType As Type = GetType(MyBaseClass(Of ))
Dim childType As Type = GetType(MyOtherChildClass)
Console.WriteLine(IsSubclassOf(childType, baseType))
'Writes: True
Here's a Microsoft Connect Ticket that might deal with this issue and give some explanation as to whether this was a feature or a bug of generic typing.
Although this case doesn't seem supported by the Type Of documentation which states that for classes, typeof will return true if:
objectexpression is of type typename or inherits from typename
I'm familiar with C# but less so with VB. However, the example VB code and example C# code appear to be different. In the VB example you use Return TypeOf Me Is MyChildClass, which in C# would be return typeof(this) is MyChildClass;. But the (supposedly working) C# example just has return this is MyChildClass;.
I would expect that TypeOf Me Is MyChildClass is asking whether the instance expression on the left (which is a Type) can be assigned to a variable declared as the type on the right (MyChildClass). Since the framework class Type has no connection to your MyChildClass this is impossible and thus a likely mistake which the compiler can catch with a warning or error--possibly the one you're getting.
Instead, I would think that the VB code should be Return Me Is MyChildClass to match the C# example, which should correctly ask if the instance Me can be assigned to a variable declared as MyChildClass. Does VB still object if this syntax is used, or does that fix the error and get the correct behavior?

Return object from class function

I have a namespace with a class inside it with another class inside that, i.e.
namespace MySpace
public class MainClass
public class SubClass
public x as integer
public z as integer
end class
public function Foo ()
dim y as SubClass
y.x = 5
return
end function
end class
end namespace
except the line y.x = 5 gets underlined with the tip "Variable y is used before it has been assigned a value. A null exception could result at runtime"
Basically I want to be able to have the multiple items that Foo assigns then available to whatever other code is using the MainClass class. What is the safest way to do this? If I was doing this in C I would have used a struct but apparently they are less efficient in VB?
You could make x and z shared:
Public Class SubClass
Public Shared x As Integer
Public Shared z As Integer
End Class
Or you can instantiate a class-level variable:
Public Class MainClass
Private m_objSubClass As New SubClass2
and call like so:
Public Function Foo()
'Shared
SubClass.x = 5
'Instantiated Class-Level variable
m_objSubClass.x = 5
End Function
If the Subclass needs to be accessed by other classes then expose the instantiated version through a Property instead of just a private variable (or return the instantiated version in your function).
Return m_objSubClass
Finally, if your values do not need to persist, you could simply instantiate a Subclass2 in the function:
Dim objSubClass2 As New SubClass2
objSubClass2.X = 5
Return objSubClass2

How to get the actual property name when referencing an interface?

Consider the following case:
Public Interface IHasDateUpdated
Property DateUpdated As DateTime
End Interface
Public Class MyClass
Implements IHasDateUpdated
Public Property MyDateUpdated As DateTime Implements IHasDateUpdated.DateUpdated
End Class
Now, assuming I was referencing an instance of MyClass as an IHasDateUpdated; how would I determine the actual name of the property that implements the interface property via reflection?
For example:
Dim x As IHasDateUpdated = New MyClass()
' How do I derive "MyDateUpdated" from "x" using x.DateUpdated?
Sorry flor the c# answer but you should be able to translate this I'm suer :)
InterfaceMapping im = y.GetInterfaceMap(typeof(IHasDateUpdated ));
foreach (MethodInfo info in im.TargetMethods)
{
if (info.IsPrivate)
MessageBox.Show(info.Name + " is private");
}
MessageBox.Show(y.FullName);

singleton pattern in vb

I am normally a c# programmer but am now working in VB for this one project when I use to set up a singleton class I would follow the Jon Skeet model
public sealed class Singleton
{
static Singleton instance = null;
static readonly object padlock = new object();
Singleton()
{
}
public static Singleton Instance
{
get
{
lock (padlock)
{
if (instance == null)
{
instance = new Singleton();
}
return instance;
}
}
}
//Added to illustrate the point
public static void a()
{
}
public void b()
{
}
}
or one of the variations now if I write the statement in c#
Singleton.Instance What procedures is all of the members that are not static, b but not a.
Now when I do the same in VB
Private Shared _instance As StackTracker
Private Shared ReadOnly _lock As Object = New Object()
Private Sub New()
_WorkingStack = New Stack(Of MethodObject)
_HistoryStack = New Queue(Of MethodObject)
End Sub
Public Shared ReadOnly Property Instance() As StackTracker
Get
SyncLock _lock
If (_instance Is Nothing) Then
_instance = New StackTracker()
End If
End SyncLock
Return _instance
End Get
End Property
I get StackTracker.Instance.Instance and it keeps going, while it is not the end of the world it looks bad.
Question is there a way in VB to hide the second instance so the user can not recursively call Instance?
Here's the full code:
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
End Class
Then to use this class, get the instance using:
Dim theSingleton As MySingleton = MySingleton.Instance
The original question was not about how to implement the singleton pattern, but referring to the fact that in C# it's a compiler error to try to access a static member via an instance. In the current VB it's a warning.
Solution:
You can change the project compiler settings to "Treat all warnings as errors", but I don't know any way to explicitly treat just warning 42025 as an error.
That being said, there is also a much simpler way to implement singletons in VB:
public class Singleton
private sub new()
end sub
public shared readonly property Instance as Singleton
get
static INST as Singleton = new Singleton
return INST
end get
end property
end class
This relies on VB thread-safe single initialization of static variables which is a feature not found in C#. The line of code beginning with the word "static" is only evaluated once even if the Instance property is accessed many times from many threads.
This is actually not the proposal put forth by Jon. You implemented the third version referenced in the article on the matter, which he points out doesn't work according to the EMCA spec due to lack of memory barriers.
Rather, you should work with the fifth version, which uses a nested class and performs the assignment of the instance in the declaration of the static field on the nested class.
If you are working in .NET 4.0, then you don't have to do any of this. You can create a static readonly field of type Lazy<T>, passing LazyThreadSafetyMode.ExecutionAndPublication to the constructor (along with your Func<T> to indicate how to create the instance) to guarantee that the value will only be created once.
Then, you expose a property which simply calls the Lazy<T>.Value property to return the lazy-loaded singleton value.
Maybe I'm missing something but I just do some variation on this, depending on what else is going on in the class:
Class MySingleton
'The instance initializes once and persists (provided it's not intentionally destroyed)
Private Shared oInstance As MySingleton = New MySingleton
'A property initialized via the Create method
Public Shared Property SomeProperty() As Object = Nothing
'Constructor cannot be called directly so prevents external instantiation
Private Sub New()
'Nothing to do
End Sub
'The property returns the single instance
Public Shared ReadOnly Property Instance As MySingleton
Get
Return oInstance
End Get
End Property
'The method returns the single instance while also initializing SomeProperty
Public Shared Function Create(
ByVal SomeParam As Object) As MySingleton
_SomeProperty = SomeParam
Return oInstance
End Function
End Class
Obviously, you would usually only provide either the Instance property or the Create method, not both (though you could if you wanted to for some reason).
In its simplest form it's:
Class MySingleton
Private Shared oInstance As MySingleton = New MySingleton
Private Sub New()
End Sub
Public Shared ReadOnly Property Instance As MySingleton
Get
Return oInstance
End Get
End Property
End Class