0

I want to implement an extension method in VB.NET that will clone an object of type T.

Say, I want

Dim cust1 as New Customer() //...
Dim cust2 as New Customer() //...

cust2 = cust1.Clone()

''
' My extension method '
''
<Runtime.CompilerServices.Extension()> _
Public Function Clone(Of T As {Class, New})(ByVal obj As T) As T 
  Dim objClone As New T
    ' clonning stuff '
    ' objClone = CType(GetAnObjClone(), T) '
  Return objClone
End Function

    Dim c As MyObject
    Dim cc As MyObject = c.Clone() ' does work!!! cool... '

question to delete.

serhio
  • 28,010
  • 62
  • 221
  • 374

2 Answers2

5

Cloning should be something the object itself performs as a regular method, not as an extension method. See the MSDN documentation on MemberwiseClone for an example.

cdhowie
  • 158,093
  • 24
  • 286
  • 300
0

I've actually done something similar using reflection, to help with modelbinding in an MVC project. I've defined a custom attribute "Updatable", and a generic extension method. This is a bit safer, as I have to explicitly mark attributes to be cloned with my custom attribute (or via a metadata class) for the behaviour to do anything, but I'm sure you could adapt it for your own purposes.

Here's my custom attribute (not very exciting!)

<AttributeUsage(AttributeTargets.Property, AllowMultiple:=False)>
Public Class UpdateableAttribute : Inherits Attribute

End Class

And here's the extension method

<Extension()>
Public Sub UpdateObject(Of T)(ByVal targetObject As T, ByVal otherObject As T)
    Dim metaDataType As MetadataTypeAttribute = GetType(T).GetCustomAttributes(GetType(MetadataTypeAttribute), True).FirstOrDefault
    If metaDataType Is Nothing Then
        For Each prop As Reflection.PropertyInfo In GetType(T).GetProperties()
            If prop.GetCustomAttributes(GetType(UpdateableAttribute), True).Count > 0 Then
                Dim targetValue = prop.GetValue(targetObject, {})
                Dim otherValue = prop.GetValue(otherObject, {})
                If Not targetValue = otherValue Then
                    prop.SetValue(targetObject, otherValue, {})
                End If
            End If
        Next
    Else
        Dim targetProps As Reflection.PropertyInfo() = GetType(T).GetProperties()
        For Each prop As Reflection.PropertyInfo In metaDataType.MetadataClassType.GetProperties()
            If prop.GetCustomAttributes(GetType(UpdateableAttribute), True).Count > 0 Then
                Dim propName As String = prop.Name
                Dim targetProperty = targetProps.Single(Function(p) p.Name = propName)
                Dim targetValue = targetProperty.GetValue(targetObject, {})
                Dim otherValue = targetProperty.GetValue(otherObject, {})
                If Not targetValue = otherValue Then
                    targetProperty.SetValue(targetObject, otherValue, {})
                End If
            End If
        Next
    End If
End Sub

Behaves nicely for me, it obviously uses reflection so isn't lightning quick but saves me a heck of a lot of left = right coding.

If you have access to the target class, you can just do this, in this example only firstname and surname will clone.

Public Class Person
    <Updateable()>
    Public Property FirstName As String
    <Updateable()>
    Public Property Surname As String
    Public Property ShoeSize As Integer
    Public Property Gender As Gender
    Public Property BirthDate As DateTime
    Public Property FavoriteCarMake
End Class

And an example using a metadata class to do the same thing, which is good for if you can't edit the target class directly (e.g. Entity Framework classes if you don't fancy editing T4 templates)

<MetadataType(GetType(PersonMetadata))>
Public Class Person
    Public Property FirstName As String
    Public Property Surname As String
    Public Property ShoeSize As Integer
    Public Property Gender As Gender
    Public Property BirthDate As DateTime
    Public Property FavoriteCarMake
End Class

Public Class PersonMetadata

    <Updateable()>
    Public Property FirstName

    <Updateable()>
    Public Property Surname

End Class

In both cases you can simply do something like this:

Dim person1 As New Person With ...
Dim person2 As New Person With ...

person1.UpdateObject(person2)
RichardW1001
  • 1,985
  • 13
  • 22