Home > OS >  Converting c# methods to a generic method
Converting c# methods to a generic method

Time:03-19

How can I convert these two ConvertDtoListToAddresses and ConvertDtoListToDocuments C# methods to a generic? I've tried passing in two generic type variables, but when I get down to 'Add' in the loop I get stuck on various errors. Converting from a DTO to its respective DBO is done in the constructor of the DBO, which I think is part of the problem.

private void ConvertDtoToPerson(PersonDTO dto)
{
    Id = dto.Id;
    Fname = dto.FirstName;
    Mname = dto.MiddleName;
    Lname = dto.LastName;
    Suffix = dto.Suffix;
    Maiden = dto.MaidenName;
    Addresses = ConvertDtoListToAddresses(dto.AddressesDto); // want to convert to generic
    Documents = ConvertDtoListToDocuments(dto.DocumentsDto); // want to convert to generic
}

private static ICollection<Address>? ConvertDtoListToAddresses(ICollection<AddressDTO>? addressesDto)
{
    if (addressesDto is not null && addressesDto.Any())
    {
        ICollection<Address> addys = new List<Address>();

        foreach (AddressDTO dto in addressesDto)
        {
            // Converts from dto in constructor
            addys.Add(new Address(dto));
        }

        return addys;
    }

    return null;
}

private static ICollection<Document>? ConvertDtoListToDocuments(ICollection<DocumentDTO>? documentsDto)
{
    if (documentsDto is not null && documentsDto.Any())
    {
        ICollection<Document> docs = new List<Document>();

        foreach (DocumentDTO dto in documentsDto)
        {
            // Converts from dto in constructor
            docs.Add(new Document(dto));
        }

        return docs;
    }

    return null;
}

Here is what I tried:

Addresses = ConvertDtoListToType<Address, AddressDTO>(dto.AddressesDto);
private static ICollection<T>? ConvertDtoListToType<T, D>(ICollection<D>? dtoCollection)
{
    if (dtoCollection is not null && dtoCollection.Any())
    {
        ICollection<T> tList = new List<T>();

        foreach (D dto in dtoCollection)
        {
            tList.Add(new T(dto));  // <-- This is where I'm having trouble
        }

        return tList;
    }

    return null;
}

CodePudding user response:

Use of a Func<D, T> factory parameter would sort this out.

private static ICollection<T>? ConvertDtoListToType<T, D>(ICollection<D>? dtoCollection, Func<D, T> factory)
{
    if (dtoCollection is not null && dtoCollection.Any())
    {
        ICollection<T> tList = new List<T>();
        foreach (D dto in dtoCollection)
        {
            tList.Add(factory(dto));
        }
        return tList;
    }
    return null;
}

Do keep in mind that that is almost the semantic equivalent of this:

private static ICollection<T>? ConvertDtoListToType<T, D>(ICollection<D>? dtoCollection, Func<D, T> factory)
    => dtoCollection?.Select(d => factory(d))?.ToList();

I'd question the idea that an empty dtoCollection should return a null final collection anyway. This is probably a better implementation.

So, having said that, your original method offers very little functionality benefit. It's code for code's sake. A simple Select/ToList pair keeps your code simple.

In any case, you can provide a static method off of Address and Document to provide the Func<D, T> that you need.

public class Address
{
    AddressDTO dto;

    public static Address CreateFromDto(AddressDTO dto)
        => new Address(dto);

    public Address(AddressDTO dto)
    {
        this.dto = dto;
    }
}

Now, calling it is like this:

var addresses = ConvertDtoListToType2(addressDtos, Address.CreateFromDto);

Or:

var addresses = addressDtos?.Select(Address.CreateFromDto)?.ToList();

CodePudding user response:

What you need is to be able to provide a constraint on the Type T to say that it has a constructor that takes a parameter of type D. Something like this:

private static ICollection<T>? ConvertDtoListToType<T, D>(
  ICollection<D>? dtoCollection) where T : new(D) 
{}

But this does not exist in C#. The workaround is to provide a factory method to create your type T given a type D. i.e. Something like:

private static ICollection<T>? ConvertDtoListToType<T, D>(
  ICollection<D>? dtoCollection, Func<D, T> factory)
{
    // Use factory(dto), instead of new T(dto)
}

But as @Zee says, you should have a look at Automapper, which can convert between types of collections.

  • Related