2016-09-14 2 views
1

У меня есть класс, который принимает числовые типы с помощью универсального типа T, я хотел бы быть в состоянии преобразовать его в CGFloat, но он бросает:Как бросить общий тип номера «T» для CGFloat

Не может вызывать инициализатор для типа 'CGFloat' с списком аргументов типа '(T)'

Что я должен сделать в своем классе, чтобы иметь возможность его успешно конвертировать?

CGFloat(self.top) - это то, что это не понравилось

типа 'Т' определяется следующим образом:

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) 
} 
extension Float: Numeric {} 
extension Double: Numeric {} 
extension Int: Numeric {} 
extension Int8: Numeric {} 
extension Int16: Numeric {} 
extension Int32: Numeric {} 
extension Int64: Numeric {} 
extension UInt: Numeric {} 
extension UInt8: Numeric {} 
extension UInt16: Numeric {} 
extension UInt32: Numeric {} 
extension UInt64: Numeric {} 

class MyClass<T: Numeric> { 
//... 
    var top:T 
} 

Когда я попытался с as, то эта ошибка во время выполнения выскочило

Не удалось отличить значение типа «Swift.Double» (0x1002b64f8) до «CoreGraphics.CGFloat» (0x1004e8740).

+0

Связанный: [Как преобразовать между связанными типами через общий инициализатор?] (Http://stackoverflow.com/questions/37556276/how-can-i-convert-between-related-types-through-a- общего инициализатор). Вы можете решить свою проблему статически, добавив «теневой метод» к вашему требованию к протоколу, например '_asOther() -> T', позволяя каждому типу, который соответствует протоколу, преобразовать себя в другой тип. – Hamish

+0

Не могли бы вы показать реализацию на основе моего примера? Я был бы рад принять этот ответ, если он будет чище/короче и т. Д. –

+0

Я не уверен, считаете ли вы его более чистым или коротким, но есть преимущества в этом, как я предложил, что я объяснено в моем ответе ниже :) – Hamish

ответ

1

В качестве дополнения to my answer here, вы можете достичь этого статически, используя «теневой метод», чтобы позволить типам Numeric принуждать себя к любому другому типу Numeric (учитывая, что инициализатор для типа назначения указан как требование протокола).

Например, вы можете определить свой протокол Numeric так:

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() } 
} 

И тогда соответствуют типы, Numeric следующим образом: использование

// 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) }} 

Пример:

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 

Это решение, вероятно, не короче, чем реализация метода , который переключается через каждый тип, который соответствует Numeric. Однако, поскольку это решение не зависит от типа выполнения, компилятор, скорее всего, получит больше возможностей для оптимизации.

Он также выгоды от статического проверки типа, что означает, что вы не можете соответствовать новому типу к Numeric без также реализует логику для преобразования этого типа к другому типу Numeric (в вашем случае, вы бы врезаться во время выполнения, если типа не обрабатывался в switch).

Кроме того, из-за инкапсуляции более гибко расширяться, поскольку логика для преобразования типов выполняется в каждом конкретном конкретном типе, который соответствует Numeric, а не одному методу, который обрабатывает возможные случаи.

+0

Я только что протестировал это и обнаружил, что CGFloat выдаст ошибку, которая не соответствует числовому протоколу, для этого вам нужно: 'extension CGFloat { public init (_ value: CGFloat) { self = value } } ' –

+0

@ Lukasz'Severiaan'Grela В Swift 3 у' CGFloat' уже есть '' public init (_ value: CGFloat) '] (https://developer.apple.com/reference/ CoreGraphics/CGFloat/1845223-INIT). Вы используете Swift 2? – Hamish

+0

Да, и я вижу некоторые предстоящие обновления: D –

1

Решение было в пределах досягаемости моей руки :) сначала посмотреть по этой ссылке:

Richard Fox - Cast-Free Arithmetic in Swift

Тогда это было так же просто, как добавить следующее имеющийся код:

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(_ value: CGFloat) 
} 
extension Numeric { 

    func convert<T: Numeric>() -> T { 
     switch self { 
     case let x as CGFloat: 
      return T(x) //T.init(x) 
     case let x as Float: 
      return T(x) 
     case let x as Double: 
      return T(x) 
     case let x as Int: 
      return T(x) 
     case let x as UInt: 
      return T(x) 
     case let x as Int8: 
      return T(x) 
     case let x as UInt8: 
      return T(x) 
     case let x as Int16: 
      return T(x) 
     case let x as UInt16: 
      return T(x) 
     case let x as Int32: 
      return T(x) 
     case let x as UInt32: 
      return T(x) 
     case let x as Int64: 
      return T(x) 
     case let x as UInt64: 
      return T(x) 
     default: 
      assert(false, "Numeric convert cast failed!") 
      return T(0) 
     } 
    } 
} 

extension CGFloat{ 
    public init(_ value: CGFloat){ 
     self = value 
    } 
} 

И затем использовать его следующим образом: let c:CGFloat = self.top.convert()

Смежные вопросы