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 wit开发者_如何学Ch 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.
精彩评论