VS 2015 Class detection from a shared method??-VBForums
Results 1 to 9 of 9

Thread: Class detection from a shared method??

  1. #1

    Thread Starter
    PowerPoster i00's Avatar
    Join Date
    Mar 2002
    Location
    1/2 way accross the galaxy.. and then some
    Posts
    2,200

    Class detection from a shared method??

    If I have this:
    VB.Net Code:
    1. Public Class Base
    2.  
    3.     Public Shared Sub Thing()
    4.         Dim CallingClassType As Type '<< Can I get this to be base OR the classes that Inherit from it??
    5.         '.... STUFF HERE
    6.     End Sub
    7.  
    8. End Class
    9.  
    10. Public Class OtherClass
    11.     Inherits Base
    12.         '.... STUFF HERE
    13. End Class
    Is it possible to work out what class called .Thing when it is called ...
    For eg:
    VB.Net Code:
    1. Base.Thing '<< the CallingClassType inside thing would be the Base Class
    2. OtherClass.Thing  '<< the CallingClassType inside thing would be the OtherClass Class

    Thanks in advance,
    Kris

  2. #2
    .NUT jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    96,845

    Re: Class detection from a shared method??

    Not possible. In fact, meaningless.

    I'm interested to know what you're actually trying to achieve. There may be a way to do it but, if so, it likely involves instance members.
    Why is my data not saved to my database? | MSDN Data Walkthroughs
    MSDN "How Do I?" Videos: VB | C#
    VBForums Database Development FAQ
    My CodeBank Submissions: VB | C#
    My Blog: Data Among Multiple Forms (3 parts)
    Beginner Tutorials: VB | C# | SQL

  3. #3
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    31,003

    Re: Class detection from a shared method??

    You could always pass the type as an argument.
    My usual boring signature: Nothing

  4. #4
    Super Moderator si_the_geek's Avatar
    Join Date
    Jul 2002
    Location
    Bristol, UK
    Posts
    39,373

    Re: Class detection from a shared method??

    One way to deal with it would be to have another version of the method inside OtherClass that overrides the version in Base, then each can 'know' the specific type.

    Like jmcilhinney I'm a bit confused as to the purpose, but I suppose it could make sense if OtherClass has other Shared methods that you want to call within Thing.

  5. #5
    You don't want to know.
    Join Date
    Aug 2010
    Posts
    4,025

    Re: Class detection from a shared method??

    You are asking some really weird questions that make me think you're either "doing something very interesting but complex" or "trying a very wrong, convoluted solution because you never asked if there's a better way".

    There are some "caller information" attributes that I thought might help, but they only get you a member name, a line number, or a file path. None of that is going to get you to a type name without some gymnastics that'd be easier if you just wrote it as:
    Code:
    Public Shared Sub Thing(caller As Object)
        Dim callerType = caller.GetType()
    
        ...
    Well, I lied. You can in SOME circumstances get it for free:
    Code:
    Public Shared Sub Thing(Of T)(caller As T)
        Dim callerType = T
    Type inference will help you a little there:

    For example:
    Code:
    Dim a As Object = New Form()
    Thing(a)
    That seems to set callerType to 'Form', though I was sort of expecting 'Object'.

    So you can't really get out of having a parameter that needs to be passed in to indicate the type. I don't think the Generic approach is any "easier" than the non-generic for this context.
    Nothing I post is production-ready. It is provided as-is, use it at your own risk.

  6. #6

    Thread Starter
    PowerPoster i00's Avatar
    Join Date
    Mar 2002
    Location
    1/2 way accross the galaxy.. and then some
    Posts
    2,200

    Re: Class detection from a shared method??

    Quote Originally Posted by Sitten Spynne View Post
    You are asking some really weird questions that make me think you're either "doing something very interesting but complex" or "trying a very wrong, convoluted solution because you never asked if there's a better way".
    I didn't think it was possible and had already used a generic type approach before you posted

    ... just a curiosity thing

    ... I wish you could have shared members and instance members with the same name and have it aware of where it was coming from...

    It would be nice to do stuff like this:

    NonInstanceThing.ClearCache 'ClearCache for ALL current instances (I track these) of NonInstanceThing... including NonInstanceThingInherited (as it is inherited from NonInstanceThing)
    NonInstanceThingInherited.ClearCache 'Clear cache for ALL NonInstanceThingInherited
    InstanceThing.ClearCache 'ClearCache for this instance only

    ... to Clear Cache that was scoped ... sorry about the cr**ppy naming

    Kris

  7. #7
    You don't want to know.
    Join Date
    Aug 2010
    Posts
    4,025

    Re: Class detection from a shared method??

    I can think of some ways to get something like what you want, but right now is a bad time for me to try and type it out. If I haven't come back and made an actual post by Tuesday, send me a PM and I'll slap my forehead and remember this thread.

    The gist:

    Look up the "Event Aggregator" pattern. If you play your cards right, you can make a thing that knows how to notify every instance of a certain class that some event has happened. If that event is "clear your cache", well...
    Nothing I post is production-ready. It is provided as-is, use it at your own risk.

  8. #8
    .NUT jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    96,845

    Re: Class detection from a shared method??

    Your original suggestion indicated that the base class should actually know something about classes derived from it, which is wrong. That would mean changing the base class if you ever derived another class from it later on, which might even be impossible if the base class is declared in a library.

    If what you want is a Shared method that will do something general when called on the base class and something more specific when called on a derived class then the thing to do is to shadow the base method in the derived method:
    vb.net Code:
    1. Public Class BaseClass
    2.  
    3.     Public Shared Sub DoSomething()
    4.         '...
    5.     End Sub
    6.  
    7. End Class
    8.  
    9.  
    10. Public Class DerivedClass
    11.     Inherits BaseClass
    12.  
    13.     Public Shared Shadows Sub DoSomething()
    14.         '...
    15.     End Sub
    16.  
    17. End Class
    The base class only has to know about the base class now and each derived class can look after itself. You will get a different implementation based on the type you call the method on. If you call it on a type that doesn't have its own implementation then you'll get the base implementation.
    Why is my data not saved to my database? | MSDN Data Walkthroughs
    MSDN "How Do I?" Videos: VB | C#
    VBForums Database Development FAQ
    My CodeBank Submissions: VB | C#
    My Blog: Data Among Multiple Forms (3 parts)
    Beginner Tutorials: VB | C# | SQL

  9. #9
    You don't want to know.
    Join Date
    Aug 2010
    Posts
    4,025

    Re: Class detection from a shared method??

    Yeah, there are two concepts here.

    One is "I would like a method to behave differently based on the runtime type of the parameter that is passed." This is a job for polymorphism via either interface or class inheritance.

    "Oh, but I want the convenience of a Shared method call without passing a parameter!" Then write an extension method.

    Here's a way to accomplish it:
    Code:
    Imports System.Runtime.CompilerServices
    Imports ConsoleApp40
    
    Module Module1
    
        Sub Main()
            Dim rng As New Random()
            For i As Integer = 0 To 9
                Dim cache As ICache
                If rng.NextDouble() < 0.5 Then
                    cache = New ExampleCache()
                Else
                    cache = New AnotherCache()
                End If
    
                cache.ClearCacheEx()
            Next
        End Sub
    
    End Module
    
    Public Interface ICache
    
        ' Property Something as the cache type here...
    
        Sub ClearCache()
    
    End Interface
    
    Public Module CacheExtensions
    
        <Extension>
        Public Sub ClearCacheEx(ByVal cache As ICache)
            Console.WriteLine("ClearCacheEx() is doing something special...")
            cache.ClearCache()
        End Sub
    
    End Module
    
    Public Class ExampleCache
        Implements ICache
    
        Public Sub ClearCache() Implements ICache.ClearCache
            Console.WriteLine("Example cache cleared!")
        End Sub
    
    End Class
    
    Public Class AnotherCache
        Implements ICache
    
        Public Sub ClearCache() Implements ICache.ClearCache
            Console.WriteLine("Other cache cleared!")
        End Sub
    
    End Class
    ClearCacheEx() is using a pattern called "Template Method", where each instance of some type has a method that does SOME of what you want, but you need to work on those results without caring about which type you have. Interface/Class polymorphism is used to let each type define what its behavior should be, and the extension method defines the "extra" parts. Using an extension method in this way means you don't have to make a Shared method call and provide a parameter.

    I do not recommend using Shadows. It breaks polymorphism which means you have to think harder about exactly what you mean to write.

    I bet you'll be surprised what happens if you run this:
    Code:
            Dim b As BaseClass = New DerivedClass()
            b.DoSomething()
    Because b is a BaseClass variable, it doesn't have access to the Shadowed instance member. So this call actually delegates to the Shared BaseClass.DoSomething(). You get a compiler warning, but now imagine this:
    Code:
    Function ClearAll(ByVal targets As IEnumerable(Of BaseClass))
        For Each t In Targets
            t.DoSomething()
    Oh. If we want it to do what we want, we have to:
    Code:
    Function ClearAll(ByVal targets As IEnumerable(Of BaseClass))
        For Each t In Targets
            If t Is DerivedClass Then
                ...
    Whoops. Shadows is almost never a good idea when you want polymorphism!

    For your second thing: "I sure do wish you could make a shared method call that does something to all instances..." There's a way. You have to track a list of active instances yourself, though. This is always a little dangerous, because if that list isn't updated when a type "dies" you'll start leaking memory.

    We use WeakReference in those cases. It's a special .NET type that holds a reference to an object in a way that does NOT cause the Garbage Collector to consider that link a root. That means if an object is only referenced by WeakReference objects, it is subject to Garbage Collection. It works kind of like a nullable type: to get at the thing on the inside you have to first ask, "Is the thing you refer to still alive?"

    So if our class tracks a Shared list of references, it can easily do something to all instances of itself:
    Code:
    Imports System.Runtime.CompilerServices
    Imports ConsoleApp40
    
    Module Module1
    
        Sub Main()
            Dim rng As New Random()
            Dim caches As New List(Of CacheBase)()
            For i As Integer = 0 To 9
                Dim cache As CacheBase
                If rng.NextDouble() < 0.5 Then
                    cache = New ExampleCache()
                Else
                    cache = New AnotherCache()
                End If
    
                caches.Add(cache)
            Next
    
            CacheBase.ClearAllCaches()
        End Sub
    
    End Module
    
    Public MustInherit Class CacheBase
        Implements IDisposable
    
        Private Shared _instances As List(Of WeakReference(Of CacheBase))
    
        Private ReadOnly _instanceIndex As Integer
    
        Public Sub New()
            If _instances Is Nothing Then
                _instances = New List(Of WeakReference(Of CacheBase))()
            End If
    
            _instances.Add(New WeakReference(Of CacheBase)(Me))
            _instanceIndex = _instances.Count - 1
        End Sub
    
        Public MustOverride Sub ClearCache()
    
        Public Shared Sub ClearAllCaches()
            For i As Integer = _instances.Count - 1 To 0 Step -1
                Dim instance As CacheBase = Nothing
                If _instances(i).TryGetTarget(instance) Then
                    instance.ClearCache()
                Else
                    _instances.RemoveAt(i)
                End If
            Next
        End Sub
    
        Public Sub Dispose() Implements IDisposable.Dispose
            Dispose(True)
        End Sub
    
        Protected Overridable Sub Dispose(disposing As Boolean)
            If disposing Then
                _instances.RemoveAt(_instanceIndex)
            End If
        End Sub
    
    End Class
    
    Public Class ExampleCache
        Inherits CacheBase
    
        Public Overrides Sub ClearCache()
            Console.WriteLine("Example cache cleared!")
        End Sub
    End Class
    
    Public Class AnotherCache
        Inherits CacheBase
    
        Public Overrides Sub ClearCache()
            Console.WriteLine("Another cache cleared!")
        End Sub
    End Class
    This is harder to do "by magic" with interfaces. I'll stop this post here so that one can stand alone as an "advanced" case.
    Last edited by Sitten Spynne; Nov 13th, 2017 at 10:47 AM.
    Nothing I post is production-ready. It is provided as-is, use it at your own risk.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Featured


Click Here to Expand Forum to Full Width

Survey posted by VBForums.