Have implemented IEquatable correctly? Should I always override GetHashCode? - vb.net

I saw the question posed here: Have I implemented Equals()/GetHashCode() correctly? but my c# is not as strong, and I am unfimiliar with IEquatable enough that I would like to see this in VB.NET if possible please.
My example code (The class will eventually use INotifyPropertyChanged when I get there):
Public Class Car
Implements ICloneable
Implements IEquatable(Of Car)
Public Property Make() As String
Get
Return m_Make
End Get
Set(ByVal value As String)
m_Make = value
End Set
End Property
Private m_Make As String
Public Property Model() As String
Get
Return m_Model
End Get
Set(ByVal value As String)
m_Model = value
End Set
End Property
Private m_Model As String
Public Function Clone() As Object Implements System.ICloneable.Clone
Return New Car() With { _
.Make = Me.Make, _
.Model = Me.Model _
}
End Function
Public Overloads Function Equals(ByVal other As Car) As Boolean Implements System.IEquatable(Of Car).Equals
Return other.Make = Me.Make AndAlso other.Model = Me.Model
End Function
End Class
Thanks,

You really do need to implement Overrides for the object.Equals and object.GetHashCode implementations.
Basically, implementing IEquatable(of T).Equals by itself will only work so long as the caller KNOWS to call IEquatable(of T).Equals instead of regular object.Equals.
Consider if you have an ArrayList of Cars and check if the list Contains(myCar), where myCar's Make and Model are the same as a car in the ArrayList...but the one in the ArrayList isn't actually the exact same instance. Contains would return false.
Worse yet, if you had a Hashtable or Dictionary, which uses GetHashCode to determine where to store entries, equality would never work because two cars with the same Make and Model would return different values for GetHashCode()
Basically, it comes down to you adding the following implementations to car:
Public Overrides Overloads Function Equals(obj As Object) As Boolean
Return TypeOf obj Is Car AndAlso Equals(DirectCast(obj, Car))
End Function
Public Overrides Function GetHashCode() As Int32
Dim hash As Int32 = 179 ' or something intelligent
hash = hash * 27 + Make.GetHashCode()
hash = hash * 27 + Model.GetHashCode()
Return hash
End Function
So the question I have is: why implement IEquatable at all? Why not just override Equals and GetHashCode?

Only implement IEquatable<T> for structs or sealed classes. Any legitimate implementation of IEquatable<T>.Equals(T) needs to have semantics compatible with the class's override of Object.GetHashCode(), which must in turn have semantics compatible with the class's override of Equals(Object). If a type is not sealed, the only way to ensure that derived types' implementation of IEquatable<T>.Equals(T) will be compatible with their override of Object.Equals(Object) will be to have the former method chain to the latter, effectively nullifying any advantage one might have obtained from implementing IEquatable<T> in the first place.
Implementing IEquatable<T> is often a big win for struct types (saves a boxing operation on every comparison), and a somewhat smaller win for other sealed types (saves a typecast on every comparison). Unless performance is critical, I'd probably skip it for most non-struct types, even if they're sealed.

Related

From A Method With In The Class Return An Instance Of The Class As An Interface Type That The Class Implements

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.

Implement IEquatable Get distinct objects

This is not working for me. I couldn't find the answer on MSDN or elsewhere after having spent too much time on it. What am I missing?
Public Class PrinterInfo
Implements IEquatable(Of PrinterInfo)
Public PrinterName As String
Public PrinterDesc As String
'default equality comparer for class vb.net
Public Overloads Function Equals(ByVal other As PrinterInfo) As Boolean _
Implements IEquatable(Of PrinterInfo).Equals
Return other.PrinterName = Me.PrinterName
End Function
End Class
Public ReadOnly Property PrinterInfoList(ByVal Normal As NormalCopier) As List(Of PrinterInfo)
Get
Dim pList1 As List(Of PrinterInfo) = GetList
pList1.Sort()
Return pList1.Distinct.ToList
End Get
End Property
I get the list just fine but I want only distinct items. I tried to implement an equality comparer but it's not working. I'm getting multiple duplicates. What do I need to do to get only distinct values?
MSDN: Enumerable.Distinct(Of TSource)
MSDN: IEqualityComparer(Of T) Interface
This seems similar but I don't understand it
I'd like to avoid Linq GroupBy if I can. That just seems clumsy to me.
The documentation for Enumerable.Distinct(Of Source) says:
The default equality comparer, Default, is used to compare values of the types that implement the IEquatable<T> generic interface. To compare a custom data type, you need to implement this interface and provide your own GetHashCode and Equals methods for the type.
That's the part you're missing. You are expected to provide a GetHashCode() implementation in your class. If you look at the code examples given, you'll see it there too. And when you think about it, it makes sense. The implementation of Distinct uses a hash set internally, so it naturally requires a proper GetHashCode implementation to function properly.
In your case, try adding this to your PrinterInfo class:
Public Overrides Function GetHashCode() As Integer
Return Me.PrinterName.GetHashCode()
End Function

Make static member persistent

I have one class with a private static (shared, since I'm in VB.BET) field and its associated public static property, since it stores one variable that should be the same to all the instances of this class.
My Class looks like this:
Public MustInherit Class NitrogenController
Private _active As Boolean
Private Shared _controlInterval As TimeSpan
Private _lastControlTime As Date
Public Property Active() As Boolean
Public Shared Property ControlInterval() As System.TimeSpan
'other properies that must be persisted
Public Function Control() As Boolean
If Not Now > _lastControlTime.Add(_controlInterval) Or Not _active Then
Return False
Else
DoControl()
_lastControlTime = Now
Return True
End If
End Function
End Class
The problem arrives when trying to binary serialize these kind of objects, since this shared field is nos being properly stored and returns to its default value when deserializing.
I suppose this is the expected behaviour, so my question is... how can I make a shared field persistent? I have read some comments to similar questions that say that this is a bad design, but it really makes sense (AFAIK) in my case, since this variable should be the same to all the object, but can be changed by the user and therefore should be stored.
Can you suggest another way of doing it?
Thanks!
What you have read, in my opinion, is correct. This is, likely, a bad design. However, if you must, there are two ways to do this with the XmlSerializer. The easy way would be to simply add a public instance (non-shared) property which has a getter and setter which simply wrap the shared property, for instance:
Public MustInherit Class NitrogenController
Public Shared Property ControlInterval As TimeSpan
Public Property CurrentControlInterval() As TimeSpan
Get
Return ControlInterval
End Get
Set(value As TimeSpan)
ControlInterval = value
End Set
End Property
End Class
If you aren't satisfied with that method, the second, more involved, option would be to override the default serialization logic by implementing the ISerializable interface.

What does VB.Net For Each Loop look at to Infer the Type

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)

Are generic operator overloads allowed in .NET 4?

I am assuming "No", but I cannot find conclusive proof on Google to back this assumption. Using keywords of 'vb.net "generic operator overload"' yields exactly 1 result, and removing 'overload' gives more, but no direct statement to the issue.
My thinking is given an abstract class, it'd be great to be able to implement a generic operator overload that a derived class can use in such a case when said operator overload has to return a New copy of the derived class, yet the code for each overload is the same. If that makes any sense.
This touches back to my previous questions on my custom Enum class and overloading the bitwise operators (And, Or, Not, & Xor), but, this particular thought was prompted by a mere curiosity of "Can it be done?".
Here's what one of my custom enums basically look like:
The parent, EBase is nothing special, just hosting common Name and Value properties, plus two shared operators, op_Equality and op_Inequality.
Friend NotInheritable Class EExample
Inherits EBase
Private Sub New()
End Sub
Friend Shared Function GetValue(ByVal Name As String) As Enums
Dim tmpOffset As Int32 = Array.IndexOf(_Names, Name)
Return If(HasContent(Name), If(tmpOffset <> -1, Values(tmpOffset), Nothing), Nothing)
End Function
' Num of Enums defined.
Friend Shared ReadOnly MaxEnums As Int32 = 5
' String literals.
Private Shared ReadOnly _Names As String() = New String() _
{"one_adam", "two_boy", "three_charles", "four_david", "five_edward"}
' Enums.
Friend Shared ReadOnly OneA As New Enums(_Names(0), 1)
Friend Shared ReadOnly TwoB As New Enums(_Names(1), 2)
Friend Shared ReadOnly ThreeC As New Enums(_Names(2), 4)
Friend Shared ReadOnly FourD As New Enums(_Names(3), 8)
Friend Shared ReadOnly FiveE As New Enums(_Names(4), 16)
' Enum Values Array.
Friend Shared ReadOnly Values As Enums() = New Enums() _
{OneA, TwoB, ThreeC, FourD, FiveE}
Friend NotInheritable Class Enums
Inherits EBase
Private Sub New()
End Sub
Friend Sub New(ByVal Name As String, ByVal Value As Int32)
MyBase.Name = Name
MyBase.Value = Value
End Sub
End Class
End Class
Here's how the things are used:
Dim Foo As EExample.Enums
Foo = EExample.TwoB
Debug.Print(Foo.Name)
will print two_boy
Now, given that, if I want to do the following:
Dim Foo as EExample.Enums
Foo = EExample.OneA Or EExample.FiveE
I have to define an operator overload for Or inside the EExample.Enums definition. How would this operator overload look?
Public Shared Operator Or(ByVal lhOp As Enums, ByVal rhOp As Enums) As Enums
Return New Enums(String.Concat(lhOp.Name, "|"c, rhOp.Name),
lhOp.Value Or rhOp.Value, True)
End Operator
I have to return a new EEXample.Enums object containing the Bitwise-Or'ed Value property of the parent EExample enums. For the name, I just concatenate the Name properties together with a pipe character until I think of something better.
Assume I have 20 enum classes similar to EExample. I have to duplicate all that operator overload code for each definition even though in the IDE, it looks the exact same. In IL, however, each overload is specific to the containing parent enum class:
.method public specialname static class MyAssembly.EExample/Enums
op_BitwiseOr(class MyAssembly.EExample/Enums lhOp,
class MyAssembly.EExample/Enums rhOp) cil managed
{ ... }
But! A generic operator overload would solve this problem if defined in EBase!
Friend Interface IEnums
Property Name As String
Property Value As Int32
End Interface
Public Shared Operator Or(Of T As IEnums)(ByVal lhOp As T, ByVal rhOp As T) As T
Return New T(String.Concat(lhOp.Name, "|"c, rhOp.Name),
lhOp.Value Or rhOp.Value, True)
End Operator
Then (in theory anyways), calling EExample.OneA Or EExample.FiveE would work because the compiler would know to call the generic operator overload from EBase, know that EExample.Enums matches the IEnums interface constraint, and automatically supply T.
That or I'm just swimming up a certain creek here without a paddle and over-analyzing things. But it's an interesting thought, no? What is StackOverflow's consensus? Do I need to lay off the Spice a little bit?
PS: I know that, in the last example, Return New T( ... ) is invalid, but I can't think of a proper syntax that would articulate the basic idea.
According to what I can see in the language specification, generic operators are not allowed. Section 9.8 says
The type of at least one of the operands or the return value must be the type that contains the operator.
and later when it describes the declaration syntax makes no accounting for a generic specifier as methods do in section 9.2.1.
Found a "workable" solution myself.
For the top-level EBase, I exposed the interface (IEnumBase) as a Friend, then created generic methods in EBase to handle the overload operators:
Protected Shared Function _
op_BitwiseOr(Of T As {IEnumBase, Class})(ByVal lhOp As T, ByVal rhOp As T, ByVal RetEnum As T) As T
RetEnum.Name = String.Concat(lhOp.Name, "|"c, rhOp.Name)
RetEnum.Value = (lhOp.Value Or rhOp.Value)
Return RetEnum
End Function
The trick here, is the generic method simply returns RetEnum back to the caller. In the derived Enums (i.e., EExample), I have:
Public Shared Shadows Operator Or(ByVal lhOp As Enums, ByVal rhOp As Enums) As Enums
Return EBase.op_BitwiseOr(lhOp, rhOp, New Enums)
End Operator
This allows me to keep the bulkier code defined once in EBase, and not replicated each time in my many derived enum classes. Those enum classes simply call on the parent's implementation and use generics to pass-in their sub-defined Enums implementation!
Yeah, not groundbreaking. I could do better, but this works well enough for my needs and doesn't over-inflate the codebase too much. It also reduces code duplication and technically makes maintenance easier, IMHO.
Still leaving Gideon Engelberth's answer as the accepted answer, however. My question initially asked if overloaded operators could be genericized, and he found the snippet on MSDN that says they can't.