I have one class implementing ISerializable interface, with one private field that is saved and retrieved:
Protected Sub New(ByVal info As SerializationInfo, ByVal context As StreamingContext)
_name= info.GetString("_name")
End Sub
Now, I want to add a new private field, but since it did not exist previously, when I try to open a serialized object I get an exception when trying to get this field, in the custom deserialization constructor. The solution I've come up with is to use a Try...Catch block, as follows:
Protected Sub New(ByVal info As SerializationInfo, _
ByVal context As StreamingContext)
_name= info.GetString("_name")
Try
_active= info.GetBoolean("_active")
Catch ex As Exception
_active= False
End Try
End Sub
It works, but it seems a bit messy to me, moreover because I will have to add a new Try...Catch block for each new field I want to add.
Is there any better way of doing this?
Thanks!
EDIT:
In this answer t0mm13b says that "There is a more neater way to do this in 2.0+ upwards". Any ideas about what is he talking about?
There is no built-in method that allows you to first determine if a particular value exists (e.g. Exists). There is also no Get... method which simply returns Nothing if the value does not exist (e.g. TryGet). However, the one way that you can do it which avoids the potential for exceptions is to use a For Each loop to iterate through all of the values in the SerializationInfo collection. For instance:
For Each i As SerializationEntry In info
Select Case i.Name
Case "_name"
_name = DirectCast(i.Value, String)
' ...
End Select
Next
Note: It may be surprising to you that you can use a For Each loop on a SerializationInfo object, since it does not implement the IEnumerable interface (nor the generic equivalent). However, For Each in VB.NET actually works with any object that has a public GetEnumerator method. So, even though it leads to some confusion, objects don't technically need to implement IEnumerable in order for you to iterate through them with a For Each loop. Since the SerializationInfo class does expose a public GetEnumerator method, you can iterate through it with a For Each loop.
Related
Specifically aimed at winforms development.
I suspect that the answer to this is probably No but S.O. has a nice way of introducing me to things I didn't know so I thought that I would ask anyway.
I have a class library with a number of defined methods therein. I know from personal experimentation that it is possible to get information about the application within which the class library is referenced. What I would like to know is whether it would be possible to get information about the value of a property of a control on a form when a routine on that form calls a method in my class library without passing a specific reference to that form as a parameter of the method in the class library?
So purely as an example (because it's the only thing I can think of off the top of my head). Is there a way that a message box (if it had been so designed to do so in the first place) could 'know' from which form a call to it had been made without that form being specifically referenced as a parameter of the message box in the first place?
Thanks for any insights you might have.
To address the example of the MessageBox, in many of the cases you can use the active form. You can retrieve it by using Form.ActiveForm. Of course, as regards the properties that you can request, you are limited to the properties provided by the Form or an interface that the Form implements and that the method in the other assembly also knows. To access other properties you can use Reflection, but this approach would neither be straightforward nor would it be clean.
In a more general scenario, you could provide the property value to the method as a parameter. If it is to complex to retrieve the value of the property and the value is not needed every time, you can provide a Func(Of TRESULT) to the method that retrieves the value like this (sample for an integer property):
Public Sub DoSomethingWithAPropertyValue(propValFunc As Func(Of Integer))
' Do something before
If propertyValueIsNeeded Then
Dim propVal = propValFunc()
End If
' Do something afterwards
End Sub
You call the method like this:
Public Sub SubInForm()
Dim x As New ClassInOtherAssembly()
x.DoSomethingWithAPropertyValue(Function() Me.IntegerProperty)
End Sub
I kind of question your intentions. There's no problem sending the information to a function or the constructor.
Instead of giving the information to the class, the class would ask for the information instead using an event.
Module Module1
Sub Main()
Dim t As New Test
AddHandler t.GetValue, AddressOf GetValue
t.ShowValue()
Console.ReadLine()
End Sub
Public Sub GetValue(ByRef retVal As Integer)
retVal = 123
End Sub
End Module
Class Test
Public Delegate Sub DelegateGetValue(ByRef retVal As Integer)
Public Event GetValue As DelegateGetValue
Public Sub ShowValue()
Dim val As Integer
RaiseEvent GetValue(val)
Console.WriteLine(val)
End Sub
End Class
this is my first post here.
I like to have some advice on designing a new module for our software.
It's written in vb6 and i want to rewrite it in vb.net. So I want to refactor it.
It does the following:
We got many templates for OpenOffice Documents. (about 300)
Each one has an unique identifier which is in a database and comes as the input.
Dending on this number i want to create the document. e.g. calc or writer or later something else.
Then call a method named doc[template_num] or sth.
I read about invoking methods. I think this as a good way to dynamically call methods by number. Tried to do this. I think I understood now how it works.
But I dont know how to handle the document type.
I want to reuse big parts of code due to all calc documents are created equal at the beginning. But filling the cells is just bit diffrent.
I dont know how to inherit and do method calls with invoke.
Maybe someone has a little code snip for me which can explain this to.
Or maybe some other good idea how to handle this problem.
I am thankful for any new thought on this.
You could, of course, just have a giant Select Case block, like this:
Public Sub CreateDoc(templateNum As Integer)
Select Case templateNum
Case 1
CreateDoc1()
Case 2
CreateDoc2()
Case 3
CreateDoc3()
End Select
End Sub
If you had a fairly small number of case statements that would probably be the better, simpler method. If however, as you described, you have many different possible ID's to look for, then that becomes impractical and I can appreciate your desire to invoke the correct method without a big Select Case block.
However, before I get into that, I feel like I should give you another option. Perhaps you don't really need to have that many different methods. Is it possible, for instance, that half of the template ID's actually all need to be handled in the same way, and the only difference is the template file name that needs to be different. If so, it is conceivable that the Select Case block wouldn't need to be that big. For instance:
Public Sub CreateDoc(templateNum As Integer)
Select Case templateNum
Case 1 To 100
CreateWriterDoc(GetTemplateFilePath(templateNum))
Case 101 To 200
CreateCalcDoc(GetTemplateFilePath(templateNum))
End Select
End Sub
Private Sub CreateWriterDoc(templateFilePath As String)
' Launch Writer with given template file
End Sub
Private Sub CreateCalcDoc(templateFilePath As String)
' Launch Calc with given template file
End Sub
Private Function GetTemplateFilePath(templateNum As Integer) As String
' Retrieve template file path for given ID
' (from DB, Array, List, Dictionary, etc.) and return it
End Sub
To me, that seems like a much simpler solution, if such a thing is possible. If not--if you really have entirely different logic which needs to be executed for each template ID, then there are several ways to do it. The first option would be to create a list of delegates which point to the appropriate method to call for each ID. For instance, you could store them in an array, like this:
Dim createDocDelegates() As Action =
{
AddressOf CreateDoc1,
AddressOf CreateDoc2,
AddressOf CreateDoc3
}
So now, those three delegates are indexed by number (0 through 2) in an array. You can invoke them by number, like this:
createDocDelegates(1).Invoke() ' Calls CreateDoc2
Or, if your ID's aren't sequential, you may want to use a Dictionary(Of Integer, Action), instead, like this:
Dim createDocDelegates As New Dictionary(Of Integer, Action)()
createDocDelegates.Add(1, AddressOf CreateDoc1)
createDocDelegates.Add(7, AddressOf CreateDoc7)
createDocDelegates.Add(20, AddressOf CreateDoc20)
Then you can call one by ID, like this:
createDocDelegates(7).Invoke() ' Calls CreateDoc7
Another option would be to create an Interface for an object that creates a document, like this:
Public Interface IDocCreator
Sub CreateDoc()
End Interface
Then you could implement a separate class for each type of template (each implementing that same interface). For instance:
Public Class Doc1Creator
Implements IDocCreator
Public Sub CreateDoc() Implements IDocCreator
' Do work
End Sub
End Class
Public Class Doc2Creator
Implements IDocCreator
Public Sub CreateDoc() Implements IDocCreator
' Do different work
End Sub
End Class
Then, you can create a list of those objects, like this:
Dim docCreators() As IDocCreator =
{
New DocCreator1(),
New DocCreator2()
}
Or:
Dim docCreators As New Dictionary(Of Integer, IDocCreator)()
docCreators.Add(1, New DocCreator1())
docCreators.Add(7, New DocCreator7())
docCreators.Add(20, New DocCreator7())
Then you can call one like this:
docCreators(1).CreateDoc()
The IDocCreator approach is very similar to the Delegate approach. Neither is the right or wrong way to do it. It depends on your style, what you are comfortable with, and your requirements. The main advantage of the IDocCreator approach is that you could easily add more properties and methods to it in the future. For instance, lets say that you also want to somewhere store a user-friendly, descriptive name for each template. It would be very easy to add a ReadOnly Property Name As String property to the IDocCreator interface, but if you go the list-of-delegates route, that would be more difficult and sloppy.
In any of the above examples, however, you still have to add the complete list of methods or delegates somewhere. So, while it's slightly less ugly than a giant Select Case block, it may still not be enough. If so, the technology you need to use is called Reflection. The System.Reflection namespace contains much of the reflection-related functionality. Reflection allows you to dynamically access and invoke portions of your code. For instance, you can use reflection to get a list of all of the properties or methods that are defined by a given class. Or you can use reflection to get a list of types that are defined by your assembly. Using reflection, it would be possible to get a method, by string name, and then invoke it. So, for instance, if you want to call the "CreateDoc1" method on the current object, you could do it like this:
Me.GetType().GetMethod("CreateDoc1").Invoke(Me, {})
Since you are calling it by its string name, you could build the method name via concatenation:
Dim methodName As String = "CreateDoc" & templateNum.ToString()
Me.GetType().GetMethod(methodName).Invoke(Me, {})
However, if you are going to use the reflection approach, instead of using the name of the method, like that, it would be cleaner, in my opinion, to use a custom attribute to tag each method. For instance, you could create an attribute class which allows you to decorate your methods like this:
<CreateDocMethod(1)>
Public Sub CreateDoc1()
' ...
End Sub
Then, at run-time, you can use reflection to find all of the methods that have that particular attribute and then invoke the right one.
The reason that I saved the discussion on reflection for last is because it is not as efficient and it can lead to brittle code. As such, it's best to only use reflection as a last-resort. There's nothing wrong with reflection, if you really need it, but, as a general rule, if there is another reasonable way to do something, which doesn't require reflection, then you probably ought to be doing it that other way.
I've been using classes for a while now, but I feel I may have been using them incorrectly.
When I create the properties for the class, I just use public variables so I end up with something like the following:
Class clsMyClass
Public Name As String
End Class
However, I've been reading some info on the net and they suggest that it should be set up in the following way:
Class clsMyClass
Private Name As String
Property UsersName() As String
Get
Return Name
End Get
Set(ByVal Value As String)
Name = Value
End Set
End Property
End Class
Is the way I'm doing it extremely incorrect? If so, why? I feel like the second method adds some sort of security but to be honest, it just looks like unnecessary code..?
One advantage of properties is that they let you customise the access to your private fields and enable you to do more so you can do the following (examples, it's not limited to that):
Make a property read-only for public access
Raise an even when a property is updated
Update other private fields when a property is updated
Validate the value that is being set
See below advantages of Properties over Variables from the C# in Depth article:
• There's more fine-grained access control with properties. Need it to be publicly gettable but really only want it set with protected access? No problem (from C# 2 onwards, at least).
• Want to break into the debugger whenever the value changes? Just add a breakpoint in the setter.
• Want to log all access? Just add logging to the getter.
• Properties are used for data binding; fields aren't.
Few other points:
1) You can also make properties read-only so no one from outside the class set the values but can fetch it.
2) You can do certain actions in the get and set. i.e. Append a prefix anytime set is called
3) You can also use auto-implemented property to minimize code like below:
Public Property Name As String
You are not doing anything wrong. Properties give you a shorthand basically, a syntactic sugar.
You can still use a backing private variable and do logic in get and set if you have to while using properties. Even better is the private/protected set or get, which is again another syntactic sugar so that you won't have to write all the code manually.
First of all, VB.NET allows you to use this syntax (called shorthand property declaration - I believe since VS 2010):
Public Property Name As String
Not so much different from this (called field declaration):
Public Name As String
Second, Microsoft data binding does not work well with fields. Try this example (see below).
Example. Put a listbox called ListBox1 (default name) and a button called Button1 on an empty form in an empty WinForms project. Replace your form code with this:
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim lst As New List(Of clsMyClass)
lst.Add(New clsMyClass)
ListBox1.ValueMember = "Name"
ListBox1.DisplayMember = "Name"
ListBox1.DataSource = lst
End Sub
End Class
Class clsMyClass
Public Property Name As String = "Hello"
End Class
Start the application and notice that a listbox is populated with one entry, Hello. This proves that binding worked correctly. Now replace your property declaration with a field declaration instead. Start your application one more time and notice that a listbox is showing your class type converted to String. It means that your field binding did not work, and default binding was used instead, where DisplayMember is assigned sort of classInstance.ToString().
If you are curious to learn more about what happens behind the scenes, you can put a breakpoint on .DataSource assignment, and see how DisplayMember gets reset or keeps its value depending on whether you are using fields or properties.
In the following example GetList returns an instance of a static (shared) variable. That one needs locking in order to be thread-safe.
But what about DoSomething which doesn't use any static variables outside the method? Does it need locking too?
EDIT: I'd like to clarify that in this particular case I expect DoSomething to always print 0-100 in sequence (i.e. no 0123745...) regardless of number of calling threads. Or, in general, that different threads don't affect each other's variables (printing to console is only an example). The language in the following example is VB.NET.
As paxdiablo said:
In this case, it appears the only thing touched is the local variable
i which would have a separate copy for every function invocation.
In other words, it wouldn't need protecting.
That is exactly what I was trying to solve. Thank you!
Public Class TestClass
Private Shared lock As New Object
Private Shared list As List(Of Integer)
Public Shared Function GetList() As List(Of Integer)
SyncLock lock
If list Is Nothing Then
list = New List(Of Integer)
End If
Return list
End SyncLock
End Function
Public Shared Sub DoSomething()
Dim i As Integer
For i = 0 To 100
Console.WriteLine(i.ToString)
Next
End Sub
End Class
Well, that would mostly depend on the language which you haven't specified but, generally, if code doesn't touch a resource that another thread can also touch, it doesn't have to be protected.
In this case, it appears the only thing touched is the local variable i which would have a separate copy for every function invocation. In other words, it wouldn't need protecting.
Of course, it could be argued that the console is also a resource and may need protection if, for example, you didn't want lines interfering with each other (synclock the write) or wanted the entire hundred lines output as a single unit (synclock the entire for loop).
But that won't really protect the console, just the block of code here that uses it. Other threads will still be able to write to the console by not using this method.
Bottom line, I don't think you need a synclock in the second method.
This section below is not relevant if you're using SyncLock in VB.Net (as now seems to be the case). The language guarantees that the lock is released no matter how you leave the block. I'll leave it in for hysterical purposes.
I'd be a little concerned about your synclock placement in the first method, especially if the return statement was a transfer of control back to the caller (and the synclock didn't automatically unlock on scope change). This looks like you can return without unlocking, which would be a disaster. I would think the following would be more suitable:
Public Shared Function GetList() As List(Of Integer)
SyncLock lock
If list Is Nothing Then
list = New List(Of Integer)
End If
End SyncLock
Return list
End Function
In general, no.
You need to be clear on the reason why GetList has the locking applied. It's not, as you imply in your first sentence, because it's returning a static variable. You could remove the locking code, and GetList would still be thread safe. But, with the locking, there's an additional guarantee - that the list will only be created once, and all callers of this code will receive a reference to the same list.
Why not avoid the lock altogether and just do this:
Public Class TestClass
Private Shared lock As New Object
Private Shared list As New List(Of Integer)
Public Shared Function GetList() As List(Of Integer)
Return list
End Function
Public Shared Sub DoSomething()
Dim i As Integer
For i = 0 To 100
Console.WriteLine(i.ToString)
Next
End Sub
End Class
This is probably a simple one but I can't seem to figure it out.
I have a bunch of form items created by the form designer declared as (in frmAquRun.Designer.vb)
Public WithEvents btnAquRunEvent1 As VisibiltyButtonLib.VisibilityButton
Public WithEvents btnAquRunEvent2 As VisibiltyButtonLib.VisibilityButton
... etc
And I basically want to be able to supply a number to a function access each of these fields. So I wrote this function. (in frmAquRun.vb)
Const EVENT_BUTTON_PREFIX As String = "btnAquRunEvent"
Public Function getEventButton(ByVal id As Integer) As Windows.Forms.Button
Dim returnButton As Windows.Forms.Button = Nothing
Try
returnButton = DirectCast(Me.GetType().InvokeMember(eventButtonName, Reflection.BindingFlags.GetField Or Reflection.BindingFlags.Public Or Reflection.BindingFlags.Instance, Nothing, Me, Nothing), Windows.Forms.Button)
Catch ex As Exception
End Try
Return returnButton
End Function
But it always seems to be generating field not found exceptions.
The message in the exception is "Field 'ATSIS_ControlProgram.frmAquRun.btnAquRunEvent1' not found.".
The namespace and form name in the message are correct. Any idea what i'm doing wrong?
The problem is that for WithEvents fields, VB actually creates a property that does the necessary event handler attaching and detaching. The generated property has the name of the field. The actual backing field gets renamed to _ + original name.1)
So in order for your code to work just prefix the button name by _ or use the BindingFlag that corresponds to the property getter (instead of GetField).
Alternatively, you can do this a lot easier by using the Controls collection of the form:
returnButton = DirectCast(Me.Controls(eventButtonName), Windows.Forms.Button)
But beware that this only works if the button is top-level, i.e. not nested within a container control on the form.
1) This is an implementation detail of the VB compiler but it’s portable (especially to Mono’s vbnc compiler) since the handling for WithEvents fields is described in great detail in the VB language specifications.
The problem is that the event handlers aren't really fields. As compiled they're really properties that implement add_btnAquRunEventX, remove_btnAquRunEventX and fire_btnAquRunEventX methods. There are ways of using reflection to get around this, but that's probably not the best way to approach the problem. Instead you can simply create a List<> and populate it with the event handlers, then index into that list.
I'm a little rusty in VB syntax but it should look something like this:
Dim events = New List<EventHandler>()
events.Add( btnAquRunEvent1 )
events.Add( btnAquRunEvent2 )
....
events( 0 )( null, EventArgs.Empty )
Take a step back though and evaluate why you're invoking by index. There may be a simpler way of abstracting the whole thing that doesn't involve all this indirection.