I have a List(Of AddlInfo) with AddlInfo being an object.
I'm trying to pass addlInfoList by reference into a function of another class:
Public Shared Sub SortAddlInfo(ByRef addlInfoList As List(Of AddlInfo))
addlInfoList.Sort(AddressOf Comparer)
End Sub
Private Function Comparer(ByVal x As AddlInfo, ByVal y As AddlInfo) As Integer
Dim result As Integer = x.AddlInfoType.CompareTo(y.AddlInfoType)
Return result
End Function
This works if I'm not passing the reference into another class, but when I try to do this, I get the following error:
Overload resolution failed because no accessible 'Sort' can be called with these arguments:
'Public Sub Sort(comparison As System.Comparison(Of AddlInfo))': 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.
'Public Sub Sort(comparer As System.Collections.Generic.IComparer(Of AddlInfo))': 'AddressOf' expression cannot be converted to 'System.Collections.Generic.IComparer(Of MyProject.AddlInfo)' because 'System.Collections.Generic.IComparer(Of MyProject.AddlInfo)' is not a delegate type.
I could put the methods back into the calling class, but I'd like to be able to call these methods from different classes within my application.
I could also instantiate a fresh List in the methods, but why? Seems silly.
Any way around this? (Or do I need to explain more?)
Thanks in advance!
Try putting your compare function into a class that implements IComparer.
Related
what I'm trying to archive with the code below is to have the GetInstance generic function take in an interface type that SystemVars implements (say IAuthentication) then create an instance of SystemVars and return it as interface type T.
The problem I an having is that no matter what casting method I try I can't find a way to return the new instance of SystemVars as T. The line in the GetInstance method Return <CastingFunction>(New SystemVars,T) always fails to compile with the error message saying Value of type SystemVars cannot be converted to 'T'.
How do I return the instance of the class as the interface type that was passed into T?
Imports System.Drawing
Public Class SystemVars
Implements IAuthentication,
IAuthorization,
IApplicationStarting
Private Sub New()
End Sub
Public Shared Function GetInstance(Of T)() As T
Return DirectCast(New SystemVars, T)
End Function
Public ReadOnly Property Username As String _
Implements IAuthentication.Username,
IAuthorization.Username
Get
Return _userName
End Get
End Property
Public ReadOnly Property Rolls As List(Of String) _
Implements IAuthorization.Rolls
Get
Return _rolls
End Get
End Property
Public ReadOnly Property InstallationId As Guid _
Implements IAuthentication.InstallationId,
IApplicationStarting.InstallationId
Get
Return _installationId
End Get
End Property
Public ReadOnly Property MainWindowStartUpPlacement As Rectangle _
Implements IApplicationStarting.MainWindowStartUpPlacement
Get
Return _mainWindowStartUpPlacement
End Get
End Property
'........
Private Shared _userName As String
Private Shared _rolls As List(Of String)
Private Shared _installationId As Guid
Private Shared _mainWindowStartUpPlacement As Rectangle
End Class
You can make an otherwise illegal cast work by passing through Object.
Public Shared Function GetInstance(Of T)() As T
Return DirectCast(CObj(New SystemVars), T)
End Function
You will get a runtime error if the cast isn't possible; as noted in the comments, this strategy is chucking type safety out the window and basically telling the compiler, "Don't bother me, I know what I'm doing." The runtime will throw an InvalidCastException on failure if you don't test and throw yourself. You can test using Type.IsAssignableFrom if you want to create a more developer-friendly error message; there isn't much context available in the debugger at the point of failure, though it may be pretty obvious if you look up the call stack.
For just three interfaces, it might be better to do three separate specific functions rather than a generic version, especially considering that the functions are necessarily Shared (and thus can't themselves be part of an interface).
You might also consider a design that includes a Dependency Injection container. In this kind of design, there would be a configuration step that would associate the interfaces with SystemVars as the implementation, then the client would ask the container for an instance of the interface and receive a SystemVars object.
The rough way that the three options (the third being to cast the SystemVars object to the requested interface) would look in code is:
'Casting a received object to a requested interface
Dim asInterface = DirectCast(SystemVars.GetInstance(), IAuthorization)
'Using a custom casting function on SystemVars
Dim asInterface = SystemVars.GetInstance(Of IAuthorization)
'Using a DI container
'Behavior if the interface isn't supported depends on the container
Dim asInterface = container.GetInstance(Of IAuthorization)
Note that TryCast could be used instead of DirectCast, in which case the result would be Nothing if the interface isn't supported.
There are examples I have seen where the return type mentioned in the function declaration is different from the default return type mentioned in the Return statement present in the body of the function. I am trying to understand how this works. For that I have come up with the following hypothetical example:
I had defined a class library MyMathsLibrary as following:
Public Class MathOperations
Public Function ADD(ByVal a As Integer, ByVal b As Integer) As Integer
Return a + b
End Function
End Class
Then I imported the above class Library into another class library named FactoryMathsLibrary as follows:
Imports MyMathsLibrary
Public Class Factory
Shared Function createMathsObject() As Object
Return New MathOperations()
End Function
End Class
Finally, I write the following code piece:
Imports FactoryMathsLibrary
Module Module1
Sub Main()
Dim a, b As Integer
Dim m As Object = Factory.createMathsObject()
Console.WriteLine(m.GetType())'O/P: MyMathsLibrary.MathOperations
Console.WriteLine("a + b = {0}", m.ADD(10, 5))
Console.WriteLine("a - b = {0}", m.Minus(20, 7))
Console.ReadLine()
End Sub
End Module
Question 1:
The function createMathsObject returns a reference which gets stored in the variable m. The output of the GetType command confirms that the reference that is returned points towards an instance of MathOperations class. So if that is the case what role does the word OBJECT play in the statement - Shared Function createMathsObject() As Object?
Related question:
Suppose there is an interface with only the ADD function in it and the class MathOperations implements the interface. In this case the variable m can be both of the Interface type as well as OBJECT type. In both cases it should be able to store the references that createMathsObject returns. In such a case, the good practice seems to be to declare m of interface type. My question is, is this because we need to avoid late binding wherever possible or is there any other reason? One drawback of declaring m to be of interface type (which could be a justification behind declaring m to be Object type) is that we cannot access elements which are not defined in the interface - for example if we declare m as object type and the interface has only the ADD method defined it it, we cannot write something like m.minus(a,b). Here MINUS should be assumed to be a function that calculates the difference between two values but is not a part of the Interface - rather its exclusive to the MathOperations class.
Is using the Named Constructor Idiom possible in VB.NET? I've found many examples in C#/C++ but can't quite wrap my head around how to use it in vb.net. Seems like a better method of keeping my code readable when involving a lot of constructors with similar argument types.
I've never heard this term before, but after a quick search it sounds vaguely like the Static Factory Pattern. The idea is you make the constructor private and use a shared (static in c#) public function to create the new object.
Public Class Foo
Private Sub New()
End Sub
Public Shared Function CreateNew(param as Object) as Foo
Dim obj as New Foo()
obj.Prop = param
return obj
End Function
End Class
You sure can make Named Constructors in VB. The pattern uses a static (Shared in VB) factory method on the class itself, so that the method can be named. (Other Factory patterns involve using a separate Factory class to provide the static method.)
System.Drawing.Color is a simple example. The pattern is implemented underneath as a static (Shared) property. Since no arguments are necessary, the Get method of a Property works just fine:
Public Shared ReadOnly Property Chartreuse As Color
Usage:
Dim favoriteColor as Color = Color.Chartreuse
Or you can make static factory methods to do the same thing.
Public Class TheClass
Public Sub New()
End Sub
Public Sub New(input As String)
'do something with input
End Sub
Public Shared Function MyNamedConstructor() As TheClass
Return New TheClass
End Function
Public Shared Function AnotherNamedConstructor() As TheClass
Return New TheClass("Another Name")
End Function
End Class
As for whether this pattern is "better" than overloading constructors, that's really an opinion. Personally, I would just overload the constructors. As you can see in the example above, the constructors need to be there anyway.
I suggest using the Named Constructor pattern when you have only a few possible ways to construct your class/struct, but consumers of your class/struct will be using those few constructors often, and with different input values to those constructors (as in the System.Drawing.Color example).
The Name in 'Named Constructor' doesn't represent a name for the constructor itself, but for the object resulting from the constructor. If your named constructor can be used to create two objects that don't feel right to give the same name to, then don't give the constructor that name.
I have a list of custom object types say
Dim a As New List(Of CustomType)
populated with instances. I have a comparer class that inherits
Public Class CustomTypeComparer
Implements IComparer(Of CustomType)
Public Function Compare(x As CustomType, y As CustomType) As Integer Implements IComparer(Of CustomType).Compare
...
End Function
End Class
that is called using the
a.Sort(New CustomTypeComparer)
method. The comparer's only method Compare() is called automatically, however occasionally the method fails because x is undefined or 'Not set to an instance of an object'.
I have scoured the list being sorted to check none of the elements are Nothing, confirmed with a watch on a.Contains(Nothing) which returns False and checked using other comparers that look at other parts of the object, none of those have problems with the list, only this one.
How can I study the problem any deeper? Is there any insight people can give on this issue?
Update:
Reading the reference source code of the framework, the list sort method uses the underlying Array.Sort() method. Taking a hint from that and I tried using the List.TrimExcess() method on the list, this has changed the behaviour and no Nothings are passed to the IComparer. A commenter discovered that IComparers are expected to compare nulls, which combines with the Array's underlying bound being greater than the array and silently having Nothings in it to generate expected functionality.
If you are just looking for debug help start CustomTypeComparer off like this
Public Class CustomTypeComparer
Implements IComparer(Of CustomType)
Public Function Compare(x As CustomType, y As CustomType) As Integer Implements IComparer(Of CustomType).Compare
If x Is Nothing Then
Stop
ElseIf y Is Nothing Then
Stop
In the following code,
For Each item in MyCollection
...
Next
What does the compiler use to determine the type of item?
For example let say I have this class, which is inheriting a non generic collection,
Public Class BaseDataObjectGenericCollection(Of T)
Inherits BaseDataObjectCollection
End Class
A for each loop still infers the Item type as Object. How would I have to modify the above class to make the type inference work?
Edit: Per Beatles1692's answer, Implementing IEnumerator(Of T) kinda works. The base class already has a GetEnumerator function, inherited from CollectionBase, so I my implementation looked like this,
Public Function GetEnumerator1() As System.Collections.Generic.IEnumerator(Of T) Implements System.Collections.Generic.IEnumerable(Of T).GetEnumerator
Return MyBase.Cast(Of T)().GetEnumerator
End Function
However, the for loop still infers the type as object. But, if I change the interface implementation to this,
Public Shadows Function GetEnumerator() As System.Collections.Generic.IEnumerator(Of T) Implements System.Collections.Generic.IEnumerable(Of T).GetEnumerator
Return MyBase.Cast(Of T)().GetEnumerator
End Function
That works, the for loop gets the type inference correct. So I guess the question is now, does For Each just look for a function called GetEnumerator ?
Well, there's only one place to go for a question like this. The spec!
Section 10.9.3 discusses For Each statements. According to it:
[if] local variable type inference is being used, then the identifier defines a new local variable whose scope is the entire For loop and whose type is the element type of the collection (Object if the enumerator expression is typed as Object).
"collection" here seems vague, but it's precisely defined on the next page. Essentially, the type must have a GetEnumerator() call, and this enumerator must (a) have a MoveNext() method that returns a boolean type, and (b) have a Current property. The type of the Current property is the type that will be inferred by the compiler. Note it actually has nothing to do with IEnumerator or IEnumerable...you just have to fit the prescribed pattern. Consider this code:
Option Infer On
Public Module M
Sub Main()
For Each x In New SomeClass()
Next
End Sub
End Module
Public Class SomeClass
Public Function GetEnumerator() As MyEnumerator
Return New MyEnumerator()
End Function
End Class
Public Class MyEnumerator
Public ReadOnly Property Current As Integer
Get
Return 42
End Get
End Property
Public Function MoveNext() As Boolean
Return True
End Function
End Class
The type of "x" in the Sub Main() is Integer, since the Current property returns Integer.
Either you should write :
For Each Item As SpecificType In MyCollection
....
Next
Then it will cast Item to SpecificType in each loop or your collection should have implemented IEnumerable(Of T)