简体   繁体   中英

Methods with 2 generic types in class with 1 generic type

I have the following:

public class P1 {
  public P2 P2 { get; set; }
  public P4 P4 { get; set; }
}

public class P2 {
  public P3 P3 { get; set; }
}

public class P3 { }

public class P4 { }

I need a Mapper for P1 as follows:

Mapper mapper = new Mapper<P1>();

But I would like to use it as follows:

Mapper<P1> mapper = Mapper.For<P1>()
  .Add(p1 => p1.P2).And(p2 => p2.P3)
  .Add(p1 => p1.P4);

So I have:

public class Mapper {
  public static Mapper<T> For<T>() {
    return new Mapper<T>();
  }
}

public class Mapper<T> {

  private List<LambdaExpression> _expressions = new List<LambdaExpression>();

  public Mapper<T> Add<K>(Expression<Func<T, K>> expression) {
    // Add expression to expressions
    return this;
  }

  public Mapper<T> And<K>(Expression<Func<T, K>> expression) {
    // Add expression to expressions
    return this;
  }

}

My problem is how to deal with Child Properties.

Note that my Mapper code only allows to do this:

  Mapper<P1> mapper = Mapper.For<P1>()
    .Add(p1 => p1.P2).And(p1 => p1.P2.P3);

And not this:

  Mapper<P1> mapper = Mapper.For<P1>()
    .Add(p1 => p1.P2).And(p2 => p2.P3);

I think the methods should return Mapper but in fact I want to create a Mapper ...

K is only a way to define the expression when calling a method.

Does anyone knows how to do this?

UPDATE

Answer with ContinuationMapper:

public class Mapper<T> {

  protected List<LambdaExpression> Paths { get; set; } = new List<LambdaExpression>();

  public ContinuationMapper<T, TCont> Add<TCont>(Expression<Func<T, TCont>> path) {

    Paths.Add(path);

    return new ContinuationMapper<T, TCont>(Paths);

  }

}

public class ContinuationMapper<TBase, TCurrent> : Mapper<TBase> {

  public ContinuationMapper(List<LambdaExpression> paths) {
    Paths = paths;
  }

  public ContinuationMapper<TBase, TNext> And<TNext>(Expression<Func<TCurrent, TNext>> path) {

    base.Paths.Add(path);

    return new ContinuationMapper<TBase, TNext>(base.Paths);

  }

}

I see the following problems with your current approach:

  • It shouldn't be possible to do an And before an Add .
  • At the end of an And (or an Add ), you have two types of type information: The base type for a new Add , and the continuation type for a new And . You cannot "store" that type of information at compile-time in a generic type with only one generic parameter.

Luckily, the type system can help here, if we split your one class in two:

public class Mapper<T>
{
    public ContinuationMapper<T, TCont> Add<TCont>(Expression<Func<T, TCont>> expression) {
        // ...
        return new ContinuationMapper<T, TCont>(...);
    }
}

public class ContinuationMapper<TBase, TCurrent> : Mapper<TBase>
{
    public ContinuationMapper<TBase, TNext> And<TNext>(Expression<Func<TCurrent, TNext>> expression) {
        // ...
        return new ContinuationMapper<TBase, TNext>(...);
    }
}

Obviously, you will have to pass the state of your expression list to the new classes, which is left as an exercise to the reader. As an added bonus, you can even make your classes immutable, if you want.

In essence you don't want to bind the method types to the class type so you need to use 2 new generic types. sort of like the following.

 public Mapper<T> Add<V,K>(Expression<Func<V, K>> expression) {
    // Add expression to expressions
    return this;
  }

  public Mapper<T> And<V,K>(Expression<Func<V, K>> expression) {
    // Add expression to expressions
    return this;
  }

Example:

Mapper<P1> mapper = Mapper.For<P1>()
 .Add((P1 p1) => p1.P2).And((P2 p2) => p2.P3)
 .Add((P1 p1) => p1.P4);

If you want the Add to be tied to the T and the and to be tied to the last property you'll need to provide back a wrapper class like

Mapper<T,V>{
public Mapper<T> Add<V>(Expression<Func<T, V>> expression) {
// Add expression to expressions
return Mapper.Wrap<T,V>(this);
}

public Mapper<T> And<V>(Expression<Func<V, K>> expression) {
    // Add expression to expressions
 return this;
   }
 }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM