Extension for Average to Return Double from Numeric Generic

Is there a way to convert any generic Numeric into a Double?

Just for purposes of syntactical illustration, here's an example of making this a generic and arriving at a Double for all three types:

func f<T:Numeric>(_ i: T) {
var d = 0.0
switch i {
case let ii as Int:
d = Double(ii)
case let ii as Int32:
d = Double(ii)
case let ii as Double:
d = ii
default:
fatalError("oops")
}
print(d)
}

But whether this is better than overloading is a matter of opinion. In my view, overloading is far better, because with the generic we are letting a bunch of unwanted types in the door. The Numeric contract is a lie. A triple set of overloads for Double, Int, and Int32 would turn the compiler into a source of truth.

Generic Method for Numeric Types

Inspired by this answer the following seems to work:

<Extension()> _
Public Function Add(Of T As {IConvertible, IComparable, IComparable(Of T),
IFormattable, IEquatable(Of T)}) _
(a As T, b As T) As Decimal
Return a.ToDecimal(Nothing) + b.ToDecimal(Nothing)
End Function

The type definition restricts the extension method close to numeric types. (Not 100%, though, as you could still call this on Byte and Date which isn't desired.)

Examples:

Dim dec As Decimal = Add(CDec(12.34), CDec(34.56))
Dim sng As Single = CSng(Add(CSng(34.56), CSng(45.67)))
Dim dbl As Double = CDbl(Add(CDbl(123.34), CDbl(45.123)))
Dim int As Integer = CInt(Add(CInt(12), CInt(34)))
Dim lng As Long = CLng(Add(CLng(1200), CLng(3400)))

Having two generic types also allows using mixed numeric types:

<Extension()> _
Public Function Add(Of T As {IConvertible, IComparable, IComparable(Of T),
IFormattable, IEquatable(Of T)},
U As {IConvertible, IComparable, IComparable(Of U),
IFormattable, IEquatable(Of U)}) _
(a As T, b As U) As Decimal
Return a.ToDecimal(Nothing) + b.ToDecimal(Nothing)
End Function

Example:

Dim mixed As Decimal = Add(CDec(12.34), CSng(23.45))

I know that this is still not ideal because there might be some loss when converting to decimal or back. Also this still requires casting the result of the function if you're not working with decimals. But it does avoid the need of duplicates in many cases.

How to cast generic number type 'T' to CGFloat

As an extension to my answer here, you could achieve this statically through using a 'shadow method' in order to allow Numeric types to coerce themselves to any other Numeric type (given that the initialiser for the destination type is listed as a protocol requirement).

For example, you could define your Numeric protocol like so:

protocol Numeric : Comparable, Equatable {

init(_ v:Float)
init(_ v:Double)
init(_ v:Int)
init(_ v:UInt)
init(_ v:Int8)
init(_ v:UInt8)
init(_ v:Int16)
init(_ v:UInt16)
init(_ v:Int32)
init(_ v:UInt32)
init(_ v:Int64)
init(_ v:UInt64)
init(_ v:CGFloat)

// 'shadow method' that allows instances of Numeric
// to coerce themselves to another Numeric type
func _asOther<T:Numeric>() -> T
}

extension Numeric {

// Default implementation of init(fromNumeric:) simply gets the inputted value
// to coerce itself to the same type as the initialiser is called on
// (the generic parameter T in _asOther() is inferred to be the same type as self)
init<T:Numeric>(fromNumeric numeric: T) { self = numeric._asOther() }
}

And then conform types to Numeric like so:

// Implementations of _asOther() – they simply call the given initialisers listed
// in the protocol requirement (it's required for them to be repeated like this,
// as the compiler won't know which initialiser you're referring to otherwise)
extension Float : Numeric {func _asOther<T:Numeric>() -> T { return T(self) }}
extension Double : Numeric {func _asOther<T:Numeric>() -> T { return T(self) }}
extension CGFloat : Numeric {func _asOther<T:Numeric>() -> T { return T(self) }}
extension Int : Numeric {func _asOther<T:Numeric>() -> T { return T(self) }}
extension Int8 : Numeric {func _asOther<T:Numeric>() -> T { return T(self) }}
extension Int16 : Numeric {func _asOther<T:Numeric>() -> T { return T(self) }}
extension Int32 : Numeric {func _asOther<T:Numeric>() -> T { return T(self) }}
extension Int64 : Numeric {func _asOther<T:Numeric>() -> T { return T(self) }}
extension UInt : Numeric {func _asOther<T:Numeric>() -> T { return T(self) }}
extension UInt8 : Numeric {func _asOther<T:Numeric>() -> T { return T(self) }}
extension UInt16 : Numeric {func _asOther<T:Numeric>() -> T { return T(self) }}
extension UInt32 : Numeric {func _asOther<T:Numeric>() -> T { return T(self) }}
extension UInt64 : Numeric {func _asOther<T:Numeric>() -> T { return T(self) }}

Example usage:

class MyClass<T : Numeric> {

var top : T

init(_ top:T) {
self.top = top
}

func topAsCGFloat() -> CGFloat {
return CGFloat(fromNumeric: top)
}
}

let m = MyClass(Int32(42))
let converted = m.topAsCGFloat()

print(type(of:converted), converted) // prints: CGFloat 42.0

This solution is probably no shorter than implementing a method that switches through every type that conforms to Numeric – however as this solution doesn't rely on runtime type-casting, the compiler will likely have more opportunities for optimisation.

It also benefits from static type-checking, meaning that you cannot conform a new type to Numeric without also implementing the logic to convert that type to another type of Numeric (in your case, you would crash at runtime if the type wasn't handled in the switch).

Furthermore, because of encapsulation, it's more flexible to expand, as the logic to convert types is done in each individual concrete type that conforms to Numeric, rather than a single method that handles the possible cases.

How can I make a generic extension on all number types in Swift?

You can extend Numeric protoocol and return Self:

extension Numeric {
var powered: Self { self * self }
}


CGFloat(3).powered // CGFloat 9
2.powered // Int 4
2.5.powered // Double 6.25
Decimal(5).powered // Decimal 25

Note that this is possible because Numeric protocol requires that the types that conform to it to implement * and *= operator functions.

How can we create a generic Array Extension that sums Number types in Swift?

As of Swift 2 it's possible to do this using protocol extensions. (See The Swift Programming Language: Protocols for more information).

First of all, the Addable protocol:

protocol Addable: IntegerLiteralConvertible {
func + (lhs: Self, rhs: Self) -> Self
}

extension Int : Addable {}
extension Double: Addable {}
// ...

Next, extend SequenceType to add sequences of Addable elements:

extension SequenceType where Generator.Element: Addable {
var sum: Generator.Element {
return reduce(0, combine: +)
}
}

Usage:

let ints = [0, 1, 2, 3]
print(ints.sum) // Prints: "6"

let doubles = [0.0, 1.0, 2.0, 3.0]
print(doubles.sum) // Prints: "6.0"

Extension method return using generics

How about this one:

    public static T Get<T>(this DataRow row, string field) where T: IConvertible 
{
if (row != null && row.Table.Columns.Contains(field))
{
object value = row[field];
if (value != null && value != DBNull.Value)
return (T)Convert.ChangeType(value, typeof(T));
}
return default(T);
}

Convert.ChangeType is much more flexible handling conversions as opposed to just casting. This pretty much reflects your original code, just generic.



Related Topics



Leave a reply



Submit