When using vb.net, if code is contained inside "< >" signs, like a namespace, what is it telling the compiler to do? Also, what would these be signs be called when used like this?
To give clarity to the question; I know that parentheses "( )" are generally used for arguments and that brackets "[ ]" are used to declare a new type, but I cannot find what the less than/greater than signs do when used in a similar capacity.
I've looked through my reference books and attempted to research this through the internet but I haven't come up with an answer. Most likely because I don't know what exactly these would be named. I always results that talk about the relational operators, which is not what I'm looking for.
Here is an example of what I'm looking at:
Imports System.ComponentModel.Design
'<CLSCompliant(True)>
<System.ComponentModel.DefaultEvent("DataReceived")> _
Public Class SerialDF1forSLCMicroCon
Inherits MfgControl.AdvancedHMI.Drivers.DF1ForSLCMicroPLC5
Implements System.ComponentModel.IComponent
Implements System.ComponentModel.ISupportInitialize
Private Shared ReadOnly EventDisposed As New Object()
Public Event Disposed As EventHandler Implements System.ComponentModel.IComponent.Disposed
Protected m_synchronizationContext As System.Threading.SynchronizationContext
Specifically I am looking at the line that contains
<System.ComponentModel.DefaultEvent("DataReceived")> _
That is an attribute. It is a way of attaching metadata (additional information) to your code that can be queried later using reflection.
For example, let's say you have a series of classes (e.g. Customer, Contact, Order, Product, etc.), each of which corresponds to a database table, and inherits from a DbTable base class that has a common DeleteAll() method.
Now, it might be that your database table names don't match your class names. In that case you can define an attribute that adds additional information to your class, providing the table name, as shown here:
<DbTableName("CUST01")>
Public Class Customer
Inherits DbTable
...
End Class
This indicates that your "Customer" objects are stored in the "CUST01" table in the database.
You might implement the attribute like this:
Public Class DbTableNameAttribute
Inherits System.Attribute
Public Property Name As String
Public Sub New(value As String)
Name = value
End Sub
End Class
Lastly, in your base DbTable class, you would implement DeleteAll() like this:
Public MustInherit Class DbTable
Public Sub DeleteAll()
' Use reflection to retrieve the attribute.
Dim attributes = Me.GetType().GetCustomAttributes()
Dim dbTableNameAttribute = attributes.FirstOrDefault(Function(x) x.GetType() = GetType(DbTableNameAttribute)
If dbTableNameAttribute IsNot Nothing Then
Dim tableName As String = CType(dbTableNameAttribute, DbTableNameAttribute).Name
' tableName will contain the value specified in the attribute (e.g. "CUST01")
Dim sql As String = "delete from " & tableName
' ... at this point you would send the delete command to your database ...
End If
End Sub
End Class
Now, in the specific example you cite: <System.ComponentModel.DefaultEvent("DataReceived")>
What is likely happening is that the SerialDF1forSLCMicroCon class probably has multiple events, and the attribute is providing a hint to the designer that the "DataReceived" event is the default one. You'll see a similar sort of thing with a Windows Forms Button. If you click the events for a Button, there are many, but the "Click" event is always highlighted by default, as it is the most commonly used one.
Related
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.
Due to new project in VBA I moved from VB.NET, to be honest don't really know how to deal between objects classes here. What I want to reach is comparing objects between different class object modules.
e.g
class Employee
properties: Name, Age
point is: compare Names between two Employees
classes: Employee and Manager
point is: compare Name from Employee with Name of Manager
I know how to in VB.NET, but how do I compare properties of different class module objects in VBA?
VBA doesn't support class polymorphism so I recommend to change the way you're thinking about the Employee and Manager classes.
You can't have an Employee class as a base class and then a separate Manager class that derives from Employee. They could be 2 separate classes implementing a common interface.
I will talk about it in detail in a bit. Let's now go through a few examples...
↓ Easy approach ↓
A base class (Person) and child classes which derive from the base class. (applies to C#, VB.NET, etc)
but in VBA you have to think of it like this:
A base class which exposes an enum property describing the position.
Something like
This is the easiest way to have a class exposing some properties. It allows you to add your Person objects into a collection and iterate over using the easy for each loop with Intellisense!
Properties comparison system would be very very easy
note: same applies to enum as its implicitly converted to a number
↓ More complex approach ↓
Two separate classes which both expose public properties. For instance you have an Employee and Manager classes which both implement a Person Interface and an additional Comparer class exposing a Compare() method
In your VBA Project you need 4 class modules and a standard module
Person (this is your Interface)
Public Property Get Name() As String
End Property
Public Property Get Age() As Long
End Property
this class is the interface which both the Employee and Manager both need to implement to share some common functions (getters for Names and Ages). Having the interface allows you to do the for each loop using the interface type variable as the enumerator. You will see in a minute.
Employee and Manager are identical. Obviously you may modify them to suit your real life solution.
Implements Person
Private name_ As String
Private age_ As Long
Public Property Get Name() As String
Name = name_
End Property
Public Property Let Name(ByVal Value As String)
name_ = Value
End Property
Public Property Get Age() As Long
Age = age_
End Property
Public Property Let Age(ByVal Value As Long)
age_ = Value
End Property
Private Property Get Person_Name() As String
Person_Name = Name
End Property
Private Property Get Person_Age() As Long
Person_Age = Age
End Property
ComparerCls you will use an instance of this class to compare two objects properties or references. You do not necessarily need to have a class for this but I prefer it that way.
Public Enum ComparisonMethod
Names = 0 ' default
Ages = 1
References = 2
End Enum
' makes names the default comparison method
Public Function Compare(ByRef obj1 As Person, _
ByRef obj2 As Person, _
Optional method As ComparisonMethod = 0) _
As Boolean
Select Case method
Case Ages
Compare = IIf(obj1.Age = obj2.Age, True, False)
Case References
Compare = IIf(obj1 Is obj2, True, False)
Case Else
Compare = IIf(obj1.Name = obj2.Name, True, False)
End Select
End Function
And your Module1 code
Option Explicit
Sub Main()
Dim emp As New Employee
emp.Name = "person"
emp.Age = 25
Dim man As New Manager
man.Name = "manager"
man.Age = 25
Dim People As New Collection
People.Add emp
People.Add man
Dim individual As Person
For Each individual In People
Debug.Print TypeName(individual), individual.Name, individual.Age
Next
End Sub
run the Main() sub and check out the results in the Immediate Window
The best part of the above code is the fact that you are creating a reference variable of the Person interface. It allows you to loop through all items in the collection that implement the interface. Also, you can use the Intellisense which is great if you have had many more properties and functions.
Comparison
Take a look again at the code in the ComparerCls class
I hope you see now why I have separated this to be a class. Its purpose is just to take care of the way the objects are being compared. You can specify the Enum order and modify the Compare() method itself to compare differently. Note the Optional parameter which allows you to call the Compare method without the method of comparison.
Now you can play around passing different parameters to the compare function. See what the results are like.
Try combinations:
emp.Name = "name"
man.Name = "name"
Comparer.Compare(emp, name, Names)
Comparer.Compare(emp, name, References)
Comparer.Compare(emp, emp, References)
If something is still unclear refer to this answer about the Implements keyword in VBA
I am trying to create a .Net DLL basically as an abstraction layer for database connections; it is going to replace a current DLL we have that is written in VB6 and I am trying to match the current functionality as much as possible.
Anyway, the essential issue I am having is that I can't find a way to get .Net classes like DataColumnCollection or DataColumn to display in the VBA Interpreter -- It may say, for example, "Column" with the type "MarshalByValueComponent," but the value will be "No Variables".
I can get it to work if I completely re-create both classes (i.e. Fields as a collection of field, which inherits from DataColumn, and then define an interface for both), but that seems like a lot of added overhead for what (should be?) a pretty simple idea. I feel like I am just missing something very simple with the way the marshaller is handling the DataColumn class.
A lot of the stuff I am finding online is on how to convert a DataTable or DataReader to a legacy ADODB Recordset, but that also would add a lot of overhead... I'd rather leave it as a DataTable and create a COM interface to allow VBA to interact with it; that way if, for example, they want to write the table to an excel sheet, I wouldn't be duplicating work (convert to ADODB recordset, then read/write to excel sheet. You'd need to iterate the entire table twice...)
Sorry for the book-length explanation -- I felt the problem needed a bit of clarification since the root-cause is trying to match legacy functionality. Here is an example of my current interface that does not work:
Public Interface IDataTable
ReadOnly Property Column As DataColumn
End Interface
<ClassInterface(ClassInterfaceType.None)> _
<System.ComponentModel.DesignerCategory("")> _
<ComDefaultInterface(GetType(Recordset.IDataTable))> _
<Guid("E7AFBBB6-CB20-44EC-9CD2-BC70B94CD8B7")> _
Public Class Recordset : Inherits Data.DataTable : Implements IDataTable
Public ReadOnly Property Column As DataColumn Implements IDataTable.Column
Get
Return MyBase.Columns(0)
End Get
End Property
Note: I originally tried the property Columns as DataColumnCollection which returned MyBase.Columns. That came through as an Object, instead of MarshalByValueComponent, but was also empty. I know MyBase.Column(0) has a value, because I can put Msgbox(MyBase.Columns(0).ColumnName) right above the return in the get and it pops up fine (don't judge; this is way easier than using a debugger for this)...
I wouldn't mind just defining them both, but I can't inherit DataColumnCollection and the COM interface already sucks at dealing with generics. Is there any other way around this without re-inventing the wheel?
Thanks for your help!
I just spent the last 3 weeks doing something eerily similar.
I ended up making two .NET assemblies:
A pure .NET assembly that talks to the datastore (for use by .NET apps).
A "COM Interop" assembly that wraps the first assembly and adds the COM overhead (ADODB references and COM-Visible interfaces).
I call the second assembly from Excel VBA using the VSTO "AddIn.Object" property.
I ended up converting System.Data.DataTables to ADODB.Recordsets as you mentioned. Getting .NET and VBA talking about anything other than primitive types was beyond-frustrating for me. In fact, I ended up serializing some objects as JSON so the two worlds could communicate.
It does seem insane, but I reinvented the wheel.
I followed this MSDN article to make my .NET code callable by VBA.
I used this Code Project article (I'm sure you've seen) to convert to Recordset*.
I let the frameworks handle string, integers, etc.
For all other data types I used Json.Net and a custom VBA class to do JSON serialization.
*Converted article to VB.Net and added some extra error handling.
Okay, this probably isn't the most elegant (or complete, at this point) solution; but I think it's the route I am going to go.
Instead of converting the whole thing to an ADODB Recordset (and duplicating any iterations), I just threw out the DataTable class entirely and wrote my own Recordset class as a COM Wrapper for the a generic Data Reader (via the IDataReader interface) and added a new Field class to manage the type conversion and set up Fields as an array of Field (since interop hates generics)
It basically creates a forward-only ADODB Recordset (same limitations) but has the benefit of only loading one row at a time, so the bulk of the data can be handled as managed code until you know what they want to do with it (I'm going to add methods for ToArray, ToAccessDB, ToFile, etc that use the reader) while still allowing the ability to iterate through the Recordset from excel/access/vbscript/vb6 (if that's really what they want to do.. mostly needed that for legacy support anyway)
Here is an example, in case anyone else has to do this again; somewhat modified for brevity:
Public Interface IRecordset
ReadOnly Property CursorPosition As Integer
ReadOnly Property FieldCount As Integer
ReadOnly Property Fields As Field()
Function ReadNext() As Boolean
Sub Close()
End Interface
<System.ComponentModel.DesignerCategory("")> _
<ClassInterface(ClassInterfaceType.None)> _
<ComDefaultInterface(GetType(IRecordset))> _
<Guid("E7AFBBB6-CB20-44EC-9CD2-BC70B94CD8B7")> _
Public Class Recordset : Implements IRecordset : Implements IDisposable
Private _Reader = Nothing
Private _FieldCount As Integer = Nothing
Private _Fields() As Field
Public ReadOnly Property CursorPosition As Integer Implements IRecordset.CursorPosition...
Public ReadOnly Property FieldCount As Integer Implements IRecordset.FieldCount...
Public ReadOnly Property Fields As Field() Implements IRecordset.Fields...
Friend Sub Load(ByVal reader As IDataReader)
_Reader = reader
_FieldCount = _Reader.FieldCount
_Fields = Array.CreateInstance(GetType(DataColumn), _FieldCount)
For i = 0 To _FieldCount - 1
_Fields(i) = New Field(i, Me)
Next
End Sub
'This logic kinda sucks and is dumb.
Public Function ReadNext() As Boolean Implements IRecordset.ReadNext
_EOF = Not _Reader.Read()
If _EOF Then Return False
_CursorPosition += 1
For i = 0 To _FieldCount - 1
_Fields(i)._Value = _Reader.GetValue(i).ToString
Next
Return True
End Function
From here you just need to define some type like Field or Column and add an interop wrapper for that type:
Public Interface IField
ReadOnly Property Name As String
ReadOnly Property Type As String
ReadOnly Property Value As Object
End Interface
<System.ComponentModel.DesignerCategory("")> _
<ClassInterface(ClassInterfaceType.None)> _
<Guid("6230C670-ED0A-48D2-9429-84820DC2BE6C")> _
<ComDefaultInterface(GetType(IField))> _
Public Class Field : Implements IField
Private Reader As IDataReader = Nothing
Private Index As Integer = Nothing
Public ReadOnly Property Name As String Implements IField.Name
Get
Return Reader.GetName(Index)
End Get
End Property
Public ReadOnly Property Value As Object Implements IField.Value
Get
Return Reader.GetValue(Index)
End Get
End Property
Public ReadOnly Property Type As String Implements IField.Type
Get
Return Reader.GetDataTypeName(Index).ToString
End Get
End Property
Sub New(ByVal i As Integer, ByRef r As IDataReader)
Reader = r
Index = i
End Sub
End Class
All of this is rather silly, but it seems to work well.
Note: I've only been using .Net for about 4 days now, so this might be terrible, please feel free to comment on anything extremely stupid I might be doing.
I am learning vb.net and I am having trouble wrapping my head around the following...
I can create several properties of a custom class and get/set values or I can create a method to set them all at once. If each property is going to allow read and write should I just make a method to assign values all at once? I assume that I am missing a very important piece here. Example:
I can create 2 properties:
Public Class Employee
Public Property LastName as string
Get
Return strLastName
End get
Set(ByVal value as string)
strLastName= value
End Set
End Property
Public Property FirstName as string
Get
Return strFirstName
End get
Set(ByVal value as string)
strFirstName= value
End Set
End Property
End Class
or I can create a method:
Public Class Employee
Public Sub AddEmployee(ByVal strLastName, ByVal strFirstName)
LastName = strLastName
FirstName = strFirstName
End Sub
End Class
I apologize for such a noob question, but any insight is greatly appreciated. thank you!
If you only have a single method, you will have to use it even if you only want to change the value of a single field.
Additionally, in such a method, if you need to validate the input, you will need to write quite a lot of code for validation that is not relevant to all of the fields.
If values must be updated together, use a method to update them together and do not provide setters.
The reality of things is that how to do this depends on what you are modelling in your class. There are no hard and fast rules that say that properties are better than methods or vice versa.
There is no reason not to support both properties and a method that sets multiple properties.
Commonly, a constructor is used to create an instance of the class and to set some properties. In VB, naming a class method "New" defines it as a constructor. In your example, if you rename your AddEmployeee method to New, you will have a perfectly fine constructor. Then you program can create new instances as such:
Dim emp1 as New Employee("Burdell", "George")
If I'm extending OnCreated for a LINQ to SQL table object, is it possible to get a reference to the data context to which the table belongs? For example, if I add a property to the data context:
Partial Class MyDataContext
Private _myValue As String
Public ReadOnly Property MyValue As String
Get
Return _myValue
End Get
Set(ByVal value As String)
_myValue = value
End Set
End Property
End Class
is there any way to access that value in the create event of the table, i.e.:
Partial Class MyTable
Private Sub OnCreated()
Dim contextValue = [data_context_reference_here].MyValue
End Sub
End Class
I don't want the property on the data context to be Shared, because it could be different per instance. I've been pouring over the designer code to figure out where the reference might be, but no luck yet. Any ideas?
There are no generated table objects in LINQ to SQL--only row objects, of which the DataContext owns one Table(Of TRowType) per. So the partial OnCreated method you implement is in a row class, and will be called whenever a row is created.
Generated row objects implement INotifyPropertyChanged and INotifyPropertyChanging, but are not derived from any base class. Since OnCreated takes no parameters, there is no way for the row to determine (via that method) which Table it belongs to, much less which DataContext it was created for.
You'll have to find some other way to do what you want.
I will say it is an incorrect pattern for Linq 2 SQL
In Linq2sql, it is supposed that any row object like your MyTable can be created without context. Later you can attach it to a table (with a specified context)
For example
Dim myTable as new MyTable()
dataContext.GetTable(Of MyTable).Attach(myTable)
So in reality, you should not create any logic inside MyTable classes that will depend on datacontext that was used to create this object, because some of them can be created without dataContexts, some can be attached and detached...
A simple google search (linq event data context) would have led you to where this question was already asked and answered:
Determine the source DataContext for a Linq to Sql query