简体   繁体   中英

Unknown Generic return type in c# method

I'm trying to create a measurement library in c# with compile-time checking of dimensions (length, time, ec.) Adding and subtracting are easy because they both used the same dimensions. However, when multiplying two scalars, the resulting Dimension (which I've used as the generic type), needs to be determined at run-time. Eg. Length x Length = Area, Area X Length = Volume, etc.

I'm only experimenting at this stage, but how would you resolve this problem (see below in method Times). The problem is that it can't convert from Area to Q in the return types.

public abstract class Dimension 
{ }

public class Length : Dimension 
{ }

public class Mass : Dimension 
{ }

public class Area : Dimension
{ }

public class Scalar<T> : Measure<T> where T : Dimension
{
    private float amount;

    public Scalar(float amount)
    {
        this.amount = amount;
    }

    public static Scalar<T> operator +(Scalar<T> A, Scalar<T> B)
    {
        return new Scalar<T>(A.amount + B.amount);
    }

    public static Scalar<T> operator -(Scalar<T> A, Scalar<T> B)
    {            
        return new Scalar<T>(A.amount - B.amount);
    }

    public Scalar<Q> Times<S>(Scalar<S> that) 
        where Q : Dimension
        where S : Dimension 
    {
        return new Scalar<Area>(this.amount * that.amount);
    }

    public override string ToString()
    {
        return "" + amount;
    }
}

To return a Scalar<Area> is a behavior that is not generic to all of Scalar<T> . For example, what if you were to multiply two Scalar<Area> instances? So you need somewhere to put that type-specific behavior. That means you have to subclass.

public class ScalarLength : Scalar<Length>
{
    public ScalarLength(float amount) : base(amount)
    {
    }
    public static Scalar<Area> operator *(ScalarLength lhs, ScalarLength rhs)
    {
        return new Scalar<Area>(lhs.amount * rhs.amount);
    }
}

Now you'll find this will work:

ScalarLength a = new ScalarLength(3);
ScalarLength b = new ScalarLength(4);
Scalar<Area> area = a * b;

Fiddle

Another approach would be to define another generic class to represent the product of two scalars, eg a Vector<T> .

public class Vector<T> where T : Dimension
{
    public Vector(Scalar<T> lhs, Scalar<T> rhs)
    {
    }
}

Then use that as the return type, using the generic type argument:

public class Scalar<T> : Measure<T> where T : Dimension
{
    /* Snip */

    static public Vector<T> operator *(Scalar<T> lhs, Scalar<T> rhs)
    {
        return new Vector<T>(lhs, rhs);
    }
}

Now this will work:

Scalar<Length> a = new Scalar<Length>(3);
Scalar<Length> b = new Scalar<Length>(4);
Vector<Length> area = a * b;

Fiddle

Try this:

public Scalar<Q> Times<S,Q>(Scalar<S> that) 
    where Q : Dimension
    where S : Dimension 
{

You're going to have to do this with standard classes:

public abstract class Dimension
{
    public Dimension(double amount)
    {
        this.Amount = amount;
    }
    
    public double Amount { get; set; }
}

public class Length : Dimension
{
    public Length(double amount) : base(amount)
    { }
    
    public static Area operator *(Length a, Length b)
    {
        return new Area(a.Amount * b.Amount);
    }   
}

public class Area : Dimension
{
    public Area(double amount) : base(amount)
    { }
}

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