admin管理员组

文章数量:1122833

I am trying to write simple mappers for my ASP.NET Core Web API project (I know there are many libraries for this, but I want to do it myself, and not depend on extra dependencies).

I was trying to handle these optional variadic arguments, like this:

public interface IMapper<TDtoType, TDbType>
{
    public TDtoType MapEntityToDto(TDbType toBeMapped, params object[] varargs);
    public TDbType MapDtoToEntity(TDtoType toBeMapped, params object[] varargs);
}

It works fine, but the problem is, that in the implementors I need to parse the varargs all the time, for example like this:

public class PositionMapper : IMapper<PositionDto, Position>
{
    public PositionDto MapEntityToDto(Position toBeMapped, params object[] varargs) => new()
    {
        StorageArea = toBeMapped.Storage.Name,
        Width = toBeMapped.Width,
        Height = toBeMapped.Height,
        Depth = toBeMapped.Depth
    };

    public Position MapDtoToEntity(PositionDto toBeMapped, params object[] varargs) => new()
    {
        Id = varargs.Length >= 2 && varargs[1] is Guid guid ? guid : Guid.Empty,
            Depth = toBeMapped.Depth,
            Width = toBeMapped.Width,
            Height = toBeMapped.Height,
            Storage = varargs[0] as Storage
                      ?? throw new ArgumentNullException(nameof(Storage), "Storage cannot be null"),
            Carrier = varargs.Length >= 3 ? varargs[2] as Carrier : null
    };
}

I would like to have a strongly typed way to add multiple arguments that can provide better intellisense and strong typing. Can anyone help with that?

I am trying to write simple mappers for my ASP.NET Core Web API project (I know there are many libraries for this, but I want to do it myself, and not depend on extra dependencies).

I was trying to handle these optional variadic arguments, like this:

public interface IMapper<TDtoType, TDbType>
{
    public TDtoType MapEntityToDto(TDbType toBeMapped, params object[] varargs);
    public TDbType MapDtoToEntity(TDtoType toBeMapped, params object[] varargs);
}

It works fine, but the problem is, that in the implementors I need to parse the varargs all the time, for example like this:

public class PositionMapper : IMapper<PositionDto, Position>
{
    public PositionDto MapEntityToDto(Position toBeMapped, params object[] varargs) => new()
    {
        StorageArea = toBeMapped.Storage.Name,
        Width = toBeMapped.Width,
        Height = toBeMapped.Height,
        Depth = toBeMapped.Depth
    };

    public Position MapDtoToEntity(PositionDto toBeMapped, params object[] varargs) => new()
    {
        Id = varargs.Length >= 2 && varargs[1] is Guid guid ? guid : Guid.Empty,
            Depth = toBeMapped.Depth,
            Width = toBeMapped.Width,
            Height = toBeMapped.Height,
            Storage = varargs[0] as Storage
                      ?? throw new ArgumentNullException(nameof(Storage), "Storage cannot be null"),
            Carrier = varargs.Length >= 3 ? varargs[2] as Carrier : null
    };
}

I would like to have a strongly typed way to add multiple arguments that can provide better intellisense and strong typing. Can anyone help with that?

Share Improve this question edited Nov 23, 2024 at 6:40 marc_s 754k183 gold badges1.4k silver badges1.5k bronze badges asked Nov 22, 2024 at 22:00 vbkristofvbkristof 11 silver badge 2
  • 3 I don't understand your problem. Because for example you are exposing signature, with parameters of type object (the most general one possible), and then inside the body you check if the first parameter is Storage. If you know in advance this is requirement, then change the signature of the function. Unless what you trying is to "how to eat a cookie and have a cookie", i.e. how to devise the most general function signature and yet have it super specific -- I don't think it is possible, those are two disjoint extremes. – greenoldman Commented Nov 23, 2024 at 5:58
  • You don't need all this. Just use extension methods and you will be fine. – Fildor Commented Nov 23, 2024 at 9:09
Add a comment  | 

2 Answers 2

Reset to default 1

You could specify the extra parameters with one extra type parameter acting as a container type for each mapping direction in the interface. The concrete parameter container types could be custom classes or structs or, as in my example below, a tuple type.

public interface IMapper<TDtoType, TDbType, TDtoParams, TEntityParams>
{
    public TDtoType MapEntityToDto(TDbType toBeMapped, TDtoParams pamameters);
    public TDbType MapDtoToEntity(TDtoType toBeMapped, TEntityParams pamameters);
}

With the following global declaration...

global using PositionParams = (Storage? storage, Carrier? carrier, Guid? id);

... we can write

public class PositionMapper : IMapper<PositionDto, Position, object, PositionParams>
{
    public PositionDto MapEntityToDto(Position toBeMapped, object parameters) => new() {
        StorageArea = toBeMapped.Storage.Name,
        Width = toBeMapped.Width,
        Height = toBeMapped.Height,
        Depth = toBeMapped.Depth
    };

    public Position MapDtoToEntity(PositionDto toBeMapped, PositionParams parameters) => new() {
        Id = parameters.id ?? Guid.Empty,
        Depth = toBeMapped.Depth,
        Width = toBeMapped.Width,
        Height = toBeMapped.Height,
        Storage = parameters.storage ?? throw new ArgumentNullException(nameof(Storage), "Storage cannot be null"),
        Carrier = parameters.carrier
    };

}

Note that I am using object as a dummy type for the unused parameters in MapEntityToDto.

Variadic C# functions are already perfectly strongly typed, exactly as with any arrays. Strongly typing can be used with polymorphism or not.

This is you who are using them without strong typing, for some reason. In your code, you defeat the purpose of strong typing by using the most base type object for the params array elements, but you don't have to do that.

To see how strong typing works, consider this:

    static void VariadicDemo(params string[] parameters) {
        if (parameters.Length > 0) {
            string name = parameters[0];
            // strongly typed array of strings
            // ...
        }
    }

    abstract class Base {
        internal abstract int Count { get; }
    }
    class Derived : Base {
        internal override int Count => 10;
    }
    class AnotherDerived : Base {
        internal override int Count => 12;
    }

    static void PolymorphicVariadicDemo(params Base[] parameters) {
        foreach (var parameter in parameters)
            System.Console.WriteLine(parameter.Count);
    }
    static void TestPolymorphicVariadicCall() {
        PolymorphicVariadicDemo(
            new Derived(),
            new AnotherDerived(),
            new Derived());
    }

In the polymorphic variant of the demo, the params array plays the role of a polymorphic set of objects. In this aspect, it is not different from any other array. The only difference is the syntactic sugar of the variadic form.

I hope it closes the issue.

本文标签: net coreHow to handle optional variadic arguments in C interfacesStack Overflow