.NET Forum / Languages / VB.NET / October 2007
Shared Method Problem With "Global" Storage
|
|
Thread rating:  |
eBob.com - 19 Oct 2007 18:01 GMT I have this nasty problem with Shared methods and what I think of as "global storage" - i.e. storage declared outside of any subroutines or functions. In the simple example below this "global" storage is ButtonHasBeenClicked. In this simple example code in Form1 calls a routine in Module1 which then calls code back in Form1 (subroutine WhatEver). WhatEver needs to access ButtonHasBeenClicked but the reference to ButtonHasBeenClicked results in the error reflected in the comment. I don't see any ambiguity in what I am trying to do. I suspect there is some syntatic solution but I have been unable to find it.
Thanks much for whatever solution and/or understanding you can provide.
Bob
Public Class Form1 Inherits System.Windows.Forms.Form
Dim ButtonHasBeenClicked As Boolean
#Region " Windows Form Designer generated code " #End Region
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click ButtonHasBeenClicked = True End Sub
Public Shared Sub WhatEver()
If ButtonHasBeenClicked Then ' Cannot refer to an instance member of a class from within a shared method or shared member initializer without an explicit instance of the class.
'do one thing Else 'do something else End If
End Sub
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click CalculateSomething()
If ButtonHasBeenClicked Then 'do something here; no problem referencing ButtonHasBeenClicked here End If End Sub
End Class
Module Module1 Public Sub CalculateSomething() Form1.WhatEver() End Sub End Module
Armin Zingler - 19 Oct 2007 18:19 GMT > I have this nasty problem with Shared methods and what I think of as > "global storage" - i.e. storage declared outside of any subroutines > or functions. It's called a "field".
> In the simple example below this "global" storage is > ButtonHasBeenClicked. In this simple example code in Form1 calls a [quoted text clipped - 14 lines] > > Dim ButtonHasBeenClicked As Boolean If it is not Shared, every instance of the Form has it's own value in this field.
> Public Shared Sub WhatEver() > > If ButtonHasBeenClicked Then ' Cannot refer to an instance > member of a class from within a shared method or shared member > initializer without an explicit instance of the class. In a shared Sub, you can not access an instance field withouth having a reference to the object. The field of which instance do you want to access? Or maybe not even one instance has been created. Therefore this fails. Don't declare the Sub as Shared.
> 'do one thing > Else 'do something else > End If > > End Sub
> Module Module1 > Public Sub CalculateSomething() > Form1.WhatEver() > End Sub > End Module If you wanted to access a Form in a Module, you would have to pass the Form to the procedure. It is better not to access the Form in a Module. Why don't you put the code into the Form? If the code in the Module is to be reused, make an abstract definition of the procedure's purpose, pass in and out the data needed, and call it from the Form.
Armin
eBob.com - 19 Oct 2007 21:50 GMT Hi Armin, Thanks for your response. Please see my further question below interspersed with the code and your comments ...
----- Original Message ----- From: "Armin Zingler" <az.nospam@freenet.de> Newsgroups: microsoft.public.dotnet.languages.vb Sent: Friday, October 19, 2007 1:19 PM Subject: Re: Shared Method Problem With "Global" Storage
>> I have this nasty problem with Shared methods and what I think of as >> "global storage" - i.e. storage declared outside of any subroutines [quoted text clipped - 36 lines] > Don't > declare the Sub as Shared. But if I don't make WhatEver Shared then I can't call it from the Module1 code.
Two things probably contribute to my confusion in this area. 1) In the real project I am working on I am using Modules simply to keep the code more manageable. The application is mining some web sites and I use a different Module for each site. Code which is common to all sites is in the Form1.vb code. And 2) apparently there can be more than one Form1, but I see Form1 as being associated with my running application. It's the "topmost thing". It wouldn't make any sense, for the (admitedly simple) applications I've written, to have more than one.
What I don't get is that if I refer to ButtonHasBeenClicked in a subroutine in the Form1.vb code there is no question about which instance is being referenced. But when I call (from the Form1.vb code) a subroutine in a Module, .Net suddenly has no knowledge of what instance the code is working on. I can solve this problem by simply cutting and pasting the subroutine in the Module to the Form1.vb code, right? (But then my Form1.vb code would get unmanageably large.)
Part of my problem, I see now, is that I have misunderstood and am misusing the Module concept. Right? But what do you do to keep the size and organization of code files more manageable? I need some way to segregate the code which is unique to mining web site A from the code which is unique to mining web site B.
Sorry to be crying on your shoulder. What I know about VB.Net is self-taught and there are major holes in my understanding.
Thanks, Bob
>> 'do one thing >> Else 'do something else [quoted text clipped - 18 lines] > > Armin Armin Zingler - 20 Oct 2007 00:30 GMT > Hi Armin, Thanks for your response. Please see my further question > below interspersed with the code and your comments ... [quoted text clipped - 80 lines] > Sorry to be crying on your shoulder. What I know about VB.Net is > self-taught and there are major holes in my understanding. Sorry, longer than intended:
Modules vs Classes ------------------- Modules are Classes. The (most important) differences are: - All members in a Module are implicitly declared Shared. - The Module is imported at project level.
Consequently, these two versions are equal:
Version M:
Module M Sub Test End Sub End Module
Version C:
Class C Shared Sub Test End Sub End Class
+ "Imports C" at project level
In both cases, you can simply write "Test" anywhere in the project in order to call Sub Test.
Shared vs instance methods -------------------------- A: Fields
If you create an object (AKA an instance of a Class), it occupies a block of memory. It consists of all the instance fields of the Class. More fields, more memory. Five instances, five times the same sized block of memory.
All Shared fields in the class are there once. Always. From the start til the end of the process. No matter how many instances exist.
-> memory consumption = 1 x shared fields + n x instance fields
B: Methods
All methods in the class are there only once. This is true for intance methods and for Shared methods. It wouldn't make sense to have them twice or more often because the code is always the same. It doesn't matter how many instances exist.
The difference between shared and instance methods is: Instance methods can access instance fields. Consequently they need a reference to an object. Shared methods can not access instance fields because they don't have a reference to an object.
Internally, the difference is: An instance method has an additional, invisible parameter called "Me". Me points to the object for which the instance method is to be executed for. By having this parameter, it is obvious that the method can access all the fields inside the object referenced by "Me". In an instance method, writing "Me." is optional, therefore writing "ButtonHasBeenClicked" is equal to "Me.ButtonHasBeenClicked".
Because a shared method does not have this invisible "Me" parameter, it can not refer to a certain object and therefore it can not access instance fields. There can be 0, 1 or 1 million objects, but the shared method wouldn't know which one to access.
Have a look at this class:
Class C Public Value As Integer
Shared Sub Test1(ByVal TheObject As C) TheObject.Value = 17 End Sub Sub Test2() Me.Value = 17 End Sub
End Class
Usage: Dim o As New C C.Test1(o) o.Test2()
Both methods do the same. It's just the object-oriented language syntax that makes us see the second call as calling a "method of the object o".
Personally I don't use modules at all, but that's personal taste. I only use shared methods in classes. The reason is that, with classes, the members are not auto-imported. As a consequence, I have to specify the class name to call the method (Classname.Method). I like it more because I will always be aware of the namespace/class structure. With modules, all member names of all modules are put into a single big (unstructured) pot. Though, you can use modules if you prefer them.
If you still have implementation questions, please ask.
Armin
eBob.com - 20 Oct 2007 03:25 GMT Armin, Thanks very much. You've cleared up a number of things for me. I am very grateful to you and Tom Shelton for your generous help. Thanks again, Bob
>> Hi Armin, Thanks for your response. Please see my further question >> below interspersed with the code and your comments ... [quoted text clipped - 191 lines] > > Armin Cor Ligthert[MVP] - 20 Oct 2007 06:33 GMT Hi Armin,
I can see that you have now 2MB connection. Creating long messages with a lot of non necessary stuff in it.
(Just a reminder to your own correct messages in past)
This is of course not only meant for Armin
:-) Cor
Armin Zingler - 20 Oct 2007 12:12 GMT > Hi Armin, > > I can see that you have now 2MB connection. Creating long messages > with a lot of non necessary stuff in it. ???
Not necessary stuff? I would say it's exactly the information he was missing. Look at his post: There was a lack of understanding of shared and instance members and what modules are. That's what I explained. Giving detailled information about the internals can sometimes also be enlighting, better than not knowing what's going on in the background forever.
As his answer shows it was not so bad.
> (Just a reminder to your own correct messages in past) > > This is of course not only meant for Armin > > :-)
:-( Armin
Cor Ligthert[MVP] - 20 Oct 2007 15:14 GMT Armin,
I found it terrible long, however when I saw it wrong, then as for ever in those cases.
Sorry,
Cor
Tom Shelton - 19 Oct 2007 18:34 GMT > I have this nasty problem with Shared methods and what I think of as "global > storage" - i.e. storage declared outside of any subroutines or functions. [quoted text clipped - 28 lines] > of a class from within a shared method or shared member initializer without > an explicit instance of the class. The error message means EXACTLY what it says. You are trying to refer to an instance variable - a variable that is associated with a specific instance of a class, in a shared method. A shared method is NOT associated with any particular instance of a class. It is associated with the class itself. VB sort of hides this distinction with it's dubious feature of allowing you to call shared methods via instance variables (an honor that C# will share as of version 3.0).
Anyway, what all of the above means is that you can not access ButtonHasBeenClicked in the manner you are attempting, since a shared method has NO knowledge of which version of ButtonHasBeenClicked to reference...
What this means is that you need to:
1) declare ButtonHasBeenClicked as shared, which means if you have multiple instances of your form - all of them will share the same ButtonHasBeenClicked, which is probably not what you want.
2) Remove the shared modifier from the sub, so that it is a member method (this means that there is an implicit Me reference passed to the sub/function - which means you can then refer to the ButtonHasBeenClicked variable as your doing now.
3) Keep your sub/function shared, but make it take a parameter that specifies the instance you are interested in: sub somesub (byval theForm as myform). Then you can use that instance variable to get a hold of ButtonHasBeenClicked.
Personally, I would probably use #2 if this sub/function has no utility beyond your specific form. And don't think that this will cause multiple versions of the funciton/sub to be in memory at once (I've seen people concerned by this before) - because it won't. All instances of the class still call the same code, but the compiler adds a additional parameter to the funciton sub, that takes the instance....
Anyway, HTH
-- Tom Shelton
eBob.com - 19 Oct 2007 22:07 GMT Hi Tom, Thanks very much for your response. Please see my further questions below interspersed with your reply. Also, I hope you will look at my reply to Armin's response.
>> I have this nasty problem with Shared methods and what I think of as >> "global [quoted text clipped - 53 lines] > multiple instances of your form - all of them will share the same > ButtonHasBeenClicked, which is probably not what you want. In my heart of hearts I KNOW that there will only be one instance of the Form1 class. So actually his solution works. But I don't like the estethics of it. It seems to be a serious misuse of the intended use of Shared.
> 2) Remove the shared modifier from the sub, so that it is a member > method (this means that there is an implicit Me reference passed to > the sub/function - which means you can then refer to the > ButtonHasBeenClicked variable as your doing now. But if I remove Shared from the subroutine then I can't call it from the Module1 code.
> 3) Keep your sub/function shared, but make it take a parameter that > specifies the instance you are interested in: sub somesub (byval > theForm as myform). Then you can use that instance variable to get a > hold of ButtonHasBeenClicked. I think that I will probably go this way. This problem will come up again when I implement my one and only message logger for my one and only Form1 application, so I might as well get used to it. Thanks again for your response. Bob
> Personally, I would probably use #2 if this sub/function has no > utility beyond your specific form. And don't think that this will [quoted text clipped - 8 lines] > -- > Tom Shelton Tom Shelton - 19 Oct 2007 23:04 GMT > Hi Tom, Thanks very much for your response. Please see my further > questions below interspersed with your reply. Also, I hope you will look at [quoted text clipped - 100 lines] > > - Show quoted text - Bob, reading both replys I'm begining to get a better understanding of what your trying to accomplish, and let me say your desire to separate common code, from implementation specific code is the correct one. But, with all due respect, you're approching it the wrong way....
First, I would separate ALL logic involved in mining the websites from the UI. That logic is more appropriately placed in a Class. In fact, I would most likely move it to it's own class library, so that the logic is able to be used from multiple different applications/ interfaces.
Second, as for separating out the logic... There are several ways to approach this, but I think the simplest at this point would to be through inheritance. In that case, I would probably create somthing looked like (air code follows!):
public mustinherit class WebMinerBase
public overridable sub DoCoolStuffButAllowSubClassesToAlterBehavior() ' common code end sub
public mustoveride sub DoCoolStuffAndInsistSubClassesProvideAnImplementation()
' factory method to create subclass to handle the address public shared function CreateWebMiner (byval address as string) as WebMinerBase if address = google then retrun new GoogleMiner() else if address = ebay then return new EbayMiner() endif end function end class
public class GoogleMiner inherits WebMinerBase
public override sub DoCoolStuffAndInsistSubClassesProvideAnImplementation() ' google specific implementation end sub end class
public class EbayMiner inherits WebMinerBase
public override sub DoCoolStuffAndInsistSubClassesProvideAnImplementation() ' Ebay specific implementation end sub end class
' client code in form
private sub button_click (byval sender as object, byval e as eventargs) handles button.click dim webminer as WebMinerBase = WebMinerBase.CreateWebMiner(addres) webminer.DoCoolStuffButAllowSubClassesToAlterBehavior() webminer.DoCoolStuffAndInsistSubClassesProvideAnImplementation() end sub
Anyway, the point is to abstract common logic into tthe base class, and the implementation specific into subclasses. Then you can use a generic interface, and through polymorphism get specific behavior...
Anyway, just a thought.
-- Tom Shelton
eBob.com - 20 Oct 2007 01:06 GMT Tom, Thanks very much for sharing your knowledge and taking the time to do so. I reallly! like your solution/approach. It would never have occurred to me as I have done very little OO programming. I have written only a few very simple classes and have never written a class which is then inherited. But I understand the concepts of your approach. I don't know if I can justify restructuring my current project. New function always seems to trump improving the structure. Thanks again for all of your help. Bob
>> Hi Tom, Thanks very much for your response. Please see my further >> questions below interspersed with your reply. Also, I hope you will look [quoted text clipped - 184 lines] > -- > Tom Shelton Tom Shelton - 20 Oct 2007 05:06 GMT > Tom, Thanks very much for sharing your knowledge and taking the time to > do so. I reallly! like your solution/approach. It would never have > occurred to me as I have done very little OO programming. That's cool. I'm happy to share.
> I have written only a few very simple classes and have never written a > class which is then inherited. OOP done right, is a really good way to approach the building of software - it is well worth studying/practicing. But, it's not always the right way either - and it can be overdone.
> But I understand the concepts of your approach. I don't know if I can > justify restructuring my current project. New function always seems to > trump improving the structure. This I have to disagree with you. I think refactoring structure, can actually lead to longer term benifits in maintainablity and extensability. If I might, I'd suggest you take a look at Martin Fowler's "Refactoring: Improving the Design of Existing Code". It's actually a pretty light read, it's only like the first 4 or 5 chapters that explain the concept - the main bulk of the book is really just a bunch of suggested refactorings. Those chapters are at least worth of a skim, to see the descriptions, so that you can recognize them in your code - but each chapter can be read in detail as you encounter that particluar "code smell" :)
> Thanks again for all of your help. Bob No problem. Happy to help.
 Signature Tom Shelton
Terry - 20 Oct 2007 16:08 GMT I think the following code will solve your problem. Having the module hard coded to Form1 makes it non-reusable. Convert your module to a class, 'WebMiningSupport' and declare all your routines as shared. Then you can reuse the code with all your projects/forms that need it. You could also solve this problem by defining an interface that all the forms that want to use the support dll must implement. Then the support routines would accept a parameter of the 'interface' type.
Public Class Form1 Dim ButtonHasBeenClicked As Boolean
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click ButtonHasBeenClicked = True End Sub
Public Sub WhatEver() If ButtonHasBeenClicked Then 'do one thing Else 'do something else End If End Sub
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click CalculateSomething(AddressOf WhatEver)
If ButtonHasBeenClicked Then 'do something here; no problem referencing ButtonHasBeenClicked here() End If End Sub
End Class
Module Module1 Delegate Sub mycallback() Public Sub CalculateSomething(ByVal theCB As mycallback) theCB() End Sub End Module
 Signature Terry
> I have this nasty problem with Shared methods and what I think of as "global > storage" - i.e. storage declared outside of any subroutines or functions. [quoted text clipped - 52 lines] > End Sub > End Module
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|