简体   繁体   中英

C# class/struct that behaves like double

I have been working on a list of records for some computations using a class that inherits from a List<> and implements some additional functions. Something like:

    public class ComplexValue
    {
        public double value;
        public string name;
        private int type;

        // More members here
    }

    public class ListOfComplexValues : List<ComplexValue>
    {
        public void UpdateValues(double parameter)
        {
            for (int i = 0; i < this.Count; i++)
            {
                this[i].value = SomeFunction(this[i].value, parameter);
            }
        }
    }

It turns out that the computations I need to make, take one or more of those lists and generate combinations of them to produce new, much larger ones but for which I only need to keep the (double) value. Given the size of those new lists I had to simplify them to be just lists of double because of memory issue and speed. So I have a new class which also implements many of the functions that were implemented for the other. The code is the same, except that in one case I use this[i] and in the other this[i].value.

    public class ListOfValues : List<double>
    {
        public void UpdateValues(double parameter)
        {
            for (int i = 0; i < this.Count; i++)
            {
                this[i] = SomeFunction(this[i], parameter);
            }
        }
    }

Is there a way I could easily merge those two implementations, for instance ensuring that the ComplexValue class behaves like a double?

Unfortunately, it's not very clear from your question what it is you actually want to do. That is, in what way do you want "that the ComplexValue class behaves like a double" ? What would the code look like, in which the ComplexValue class is used but behaves like a double ?

As you're aware I'm sure, you cannot inherit double , because it's a value type. So polymorphism is not an option here, at least not on the value type itself. But if you are willing to take a step back and reevaluate what led you to the List<T> -derived classes in the first place, perhaps there are options that would work for you.

In my experience, it's very unusual for inheriting a collection type to be the correct approach; only when the subclass is doing genuine collection -oriented things, and only collection-oriented things, would subclassing a collection type be appropriate. Otherwise, it usually makes more sense to put the non-collection-oriented operations into some other class that has a collection type (like the List<T> object).

In any case (ie whether you compose or inherit), you could use some kind of accessor delegates to handle the disparity between collections of List<double> and of List<ComplexValue> .

For example (for the sake of clarity, I'm keeping the inherit approach here, even though IMHO composing is likely to be better):

class ListOfValues<T> : List<T>
{
    private readonly Func<List<T>, int, double> _getter;
    private readonly Action<List<T>, int, double> _setter;

    public ListOfTValues(
        Func<List<T>, int, double> getter, Action<List<T>, int, double> setter)
    {
        _getter = getter;
        _setter = setter;
    }

    public void UpdateValues(double parameter)
    {
        for (int i = 0; i < this.Count; i++)
        {
            _setter(this, i, SomeFunction(_getter(this, i), parameter));
        }
    }
}

Then you could create your list object like this:

ListOfValues<double> doubleList = new ListOfValues<double>(
    (list, i) => list[i], (list, i, value) => list[i] = value);

ListOfValues<ComplexValue> doubleList = new ListOfValues<ComplexValue>(
    (list, i) => list[i].value, (list, i, value) => list[i].value = value);

Another alternative would be to do something similar to the above — ie provide accessor methods to map the collection type to the actual computational code — but to provide them as virtual method overrides rather than delegates:

abstract class ListOfValues<T> : List<T>
{
    protected abstract double GetItemValue(int i);
    protected abstract void SetItemValue(int i, double value);

    public void UpdateValues(double parameter)
    {
        for (int i = 0; i < this.Count; i++)
        {
            SetItemValue(, i, SomeFunction(GetItemValue(i), parameter));
        }
    }
}

class ListOfDouble : ListOfValues<double>
{
    protected override double GetItemValue(int i)
    {
        return this[i];
    }

    protected override void SetItemValue(int i, double value)
    {
        this[i] = value;
    }
}

class ListOfComplexValue : ListOfValues<ComplexValue>
{
    protected override double GetItemValue(int i)
    {
        return this[i].value;
    }

    protected override void SetItemValue(int i, double value)
    {
        this[i].value = value;
    }
}

Note that all of the above could be implemented without inheriting the List<T> type. You can put the computation logic into a class that simply contains an instance of List<T> instead. Finally, note that all of the above still works even if you follow the advice that Jon provided in his first comment to your question (advice that I think is good and worth following).

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