2011-03-22 19 views
5

¿Cómo puedo crear una función que devuelve un objeto de un tipo determinado, que el usuario de la función especifica en el argumento (utilizando vb.net 2010)?Función que devuelve un objeto con el tipo especificado en el argumento

Private Function TryThis(ByVal t As Type) As Object 
    Dim n = New t 
    Return n 
End Function 

El código anterior no funciona, pero tal vez pueda explicar lo que quiero lograr.

Usando esta característica, quiero hidratar mis objetos de transferencia de datos de la tabla de datos. El cliente simplemente llamará a esta función, especificando qué DTO quiere el cliente, y esta función simplemente creará ese DTO y poblará las propiedades con el reflejo GetType.GetProperties().

Respuesta

4

Aquí hay un ejemplo muy básico que usa genéricos. El método GetPropFromDatabase solo usa un Select Case pero en lugar de eso obviamente pondría su llamada real de búsqueda en la base de datos.

Option Explicit On 
Option Strict On 

Public Class Form1 
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load 
     Dim X = CreateObject(Of TestClass)() 

     Trace.WriteLine(X.PropertyA) 
     Trace.WriteLine(X.PropertyB) 

     Me.Close() 
    End Sub 

    ''//Of T as New means that the object must have a constructor 
    Private Function CreateObject(Of T As New)() As T 
     ''//Create our object 
     Dim O As New T 
     ''//Get the type properties 
     Dim Props = GetType(T).GetProperties() 
     ''//Loop through each property 
     For Each P In Props 
      ''//Set the value of our return type by property name 
      P.SetValue(O, GetPropFromDatabase(P.Name), Nothing) 
     Next 
     ''//Return our object 
     Return O 
    End Function 
    ''//This function would obviously do a lot more 
    Private Shared Function GetPropFromDatabase(ByVal name As String) As String 
     Select Case name 
      Case "PropertyA" 
       Return "Value1" 
      Case "PropertyB" 
       Return "Value2" 
     End Select 

     Throw New ApplicationException(String.Format("Unknown database column : {0}", name)) 
    End Function 
End Class 
Public Class TestClass 
    Public Property PropertyA As String 
    Public Property PropertyB As String 
End Class 

EDITAR

Puede que tenga que jugar con BindingFlags en GetProperties() dependiendo de cómo el objeto es de configuración.

EDIT 2

También puede ser que desee ver en el uso de atributos personalizados. Por ejemplo, si tiene una columna en su base de datos llamada [First Name] que obviamente no puede existir como una propiedad del objeto debido al espacio. Con un atributo personalizado puede marcar ciertas propiedades para ignorarlas o analizarlas de una manera especial. El siguiente código muestra una versión extendida del código anterior.

Option Explicit On 
Option Strict On 

Public Class Form1 
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load 
     Dim X = CreateObject(Of TestClass)() 

     Trace.WriteLine(X.PropertyA) 
     Trace.WriteLine(X.PropertyB) 
     Trace.WriteLine(X.FirstName) 

     Me.Close() 
    End Sub 

    ''//Of T as New means that the object must have a constructor 
    Private Function CreateObject(Of T As New)() As T 
     ''//Create our object 
     Dim O As New T 
     ''//Get the type properties 
     Dim Props = GetType(T).GetProperties() 

     ''//Will hold the name of the database column to get the value of 
     Dim PropName As String 
     ''//Will hold our collection of attributes on the property 
     Dim CustomAttributes() As Object 
     ''//Loop through each property 
     For Each P In Props 
      ''//Default the value to the property name 
      PropName = P.Name 
      ''//Try to get any custom attributes for the property 
      CustomAttributes = P.GetCustomAttributes(True) 
      ''//See if we have anything to work with 
      If (CustomAttributes IsNot Nothing) AndAlso (CustomAttributes.Count > 0) Then 
       ''//Loop through each attribute 
       For Each A In CustomAttributes 
        ''//If the attribute is our custom one defined below 
        If TypeOf A Is ColumnNameInDatabase Then 
         ''//Use the manually set column name instead 
         PropName = DirectCast(A, ColumnNameInDatabase).ColumnNameInDatabase 
         ''//No reason to loop through any more attributes so exit 
         Exit For 
        End If 
       Next 
      End If 
      ''//Set the value of our return type by property name 
      P.SetValue(O, GetPropFromDatabase(PropName), Nothing) 
     Next 
     ''//Return our object 
     Return O 
    End Function 
    ''//This function would obviously do a lot more 
    Private Shared Function GetPropFromDatabase(ByVal name As String) As String 
     Select Case name 
      Case "PropertyA" 
       Return "Value1" 
      Case "PropertyB" 
       Return "Value2" 
      Case "First Name" 
       Return "Bob Dole" 
     End Select 

     Throw New ApplicationException(String.Format("Unknown database column : {0}", name)) 
    End Function 
End Class 
Public Class TestClass 
    Public Property PropertyA As String 
    Public Property PropertyB As String 
    <ColumnNameInDatabase("First Name")> Public Property FirstName As String 
End Class 
Public Class ColumnNameInDatabase 
    Inherits Attribute 
    Private _ColumnNameInDatabase As String 
    Public ReadOnly Property ColumnNameInDatabase As String 
     Get 
      Return Me._ColumnNameInDatabase 
     End Get 
    End Property 
    Public Sub New(ByVal columnNameInDatabase As String) 
     Me._ColumnNameInDatabase = columnNameInDatabase 
    End Sub 
End Class 
1

Gracias Chris Haas por las palabras clave, pude investigar el tema más a fondo. Encontré another answer que no usa Of T As New, que finalmente uso porque mis DTO no declaran ningún constructor.

Para el registro, esto es lo que finalmente se utilizo:

Private Function DataRowToObject(Of T)(ByVal source As DataRow) As T 
    Dim destination = GetType(T).GetConstructor(System.Type.EmptyTypes).Invoke({}) 
    Dim props = destination.GetType.GetProperties() 
    Dim propnames = props.Select(Function(x) x.Name.ToLower) 

    For Each col In source.Table.Columns 
     Dim colname = col.ColumnName.ToLower() 
     If propnames.Contains(colname) Then 
      Dim prop = props.Where(Function(x) x.Name.ToLower = colname).First 
      If IsDBNull(source(col)) And prop.PropertyType Is GetType(String) Then 
       prop.SetValue(destination, "", Nothing) 
      ElseIf IsDBNull(source(col)) Then 
       prop.SetValue(destination, Nothing, Nothing) 
      Else 
       prop.SetValue(destination, source(col), Nothing) 
      End If 
     End If 
    Next 

    Return destination 
End Function 

Protected Function DataTableToList(Of T)(ByVal source As DataTable) As IList(Of T) 
    Dim destination As New List(Of T) 

    For Each row In source.Rows 
     Dim obj = DataRowToObject(Of T)(row) 
     destination.Add(obj) 
    Next 

    Return destination 
End Function 

Aquí es un cliente que utilice la función anterior:

Public Function PeopleAll() As IList(Of DTO.People) 
    Dim ResultTbl As New DataTable 
    adp.TableLoad("select * from people", ResultTbl) 

    Dim result = DataTableToList(Of DTO.People)(ResultTbl) 
    Return result 
End Function 

He aquí una muestra de mi objeto de transferencia de datos:

Public Class People 
    Public Overridable Property ID As Int64 
    Public Overridable Property Name As String 
    Public Overridable Property Email As String 
End Class 
Cuestiones relacionadas