Reputation: 75
Now I know that C# doesn't support inheritance of structs for a number of reasons. I was wondering however, if it is still possible to implement some form of hierarchy for data types such that a function can rely on a variable of type B or type C having field x because they both inherit from type A.
More specific: I have two structs that have a value field and coordinates (2D and 3D)
public struct BarDataPoint
{
public TwoTuple<float> coords { get; }
public float value { get; }
public BarDataPoint(TwoTuple<float> coords, float value)
{
this.coords = coords;
this.value = value;
}
}
public struct ScatterDataPoint
{
public ThreeTuple<float> coords { get; }
public float value { get; }
public ScatterDataPoint(ThreeTuple<float> coords, float value)
{
this.coords = coords;
this.value = value;
}
}
I also have this function that is supposed to find the maximum value of an array of data points
{
// Finds the maximum value of an BarData array
float maxValue = data[0].value;
foreach (BarDataPoint dataPoint in data)
{
maxValue = (maxValue < dataPoint.value) ? dataPoint.value : maxValue;
}
return maxValue;
}
I would like to generalize this function, so that it can take both types (or any other type with a "value" field) and return its maximum value.
Is this possible? If yes, how? Or would you recommend a completely different design?
Upvotes: 0
Views: 243
Reputation: 20354
There are no problems having a struct implement an interface:
public interface IDataPoint
{
float Value { get; }
}
public struct BarDataPoint : IDataPoint
public struct ScatterDataPoint : IDataPoint
Then you could do this:
{
float maxValue = data[0].Value;
foreach (IDataPoint dataPoint in data)
{
maxValue = (maxValue < dataPoint.Value) ? dataPoint.Value : maxValue;
}
return maxValue;
}
To avoid the performance hit caused by boxing in the above example, you could make the implementing method generic:
float GetMaxValue<TDataPoint>(TDataPoint[] data) where TDataPoint : IDataPoint
{
float maxValue = data[0].Value;
foreach (TDataPoint dataPoint in data)
{
maxValue = (maxValue < dataPoint.Value) ? dataPoint.Value : maxValue;
}
return maxValue;
}
Just be sure to always refer to data as TDataPoint
within the generic method, as using IDataPoint
will cause boxing (because interfaces are reference types).
The reason this works is because the generic method will be resolved to either float GetMaxValue(BarDataPoint[] data)
or float GetMaxValue(ScatterDataPoint[] data)
and not float GetMaxValue(IDataPoint[] data)
.
As an aside, if you simply need to know the maximum value, Enumerable.Max
should do exactly what you're after, without the need for the above approach:
BarDataPoint[] data = //...
float max = data.Max(x => x.Value);
Upvotes: 4