2015-01-07 52 views
0

最近我在Swift中試驗了賦值運算符,並遇到了一個問題,我找不到解決方案。我可以使用自定義賦值運算符初始化變量嗎?

考慮以下結構:

struct Box<T> { 

    let value: T 

} 

struct MyStruct { 

    let myProperty: String 

    init(boxedProperty: Box<String>) { 
     self.myProperty = boxedProperty.value 
    } 

} 

似乎很簡單,但想象MyStruct接受20個盒裝的性質 - 他們每個人都需要通過訪問Box實例的value財產「開箱」。現在想象擁有MyStruct的20個版本。

這將導致數百.value線,這是相當多的。相反,爲了減少代碼混亂,我想使用一個自定義賦值運算符,它隱式地「解開」Box並將其值賦給一個變量。

考慮這個簡單的例子(沒有可選性支持等):

infix operator <|= { 
    associativity right 
    precedence 90 
    assignment 
} 

func <|= <T>(inout lhs: T, rhs: Box<T>) { 
    lhs = rhs.value 
} 

理想情況下,我想用<|=操作向右走,像這樣:

init(boxedProperty: Box<String>) { 
    self.myProperty <|= boxedProperty 
} 

但不幸的是(和相當可預測?),這是行不通的,因爲myProperty變量在用於函數之前未初始化:

error: variable 'self.myProperty' passed by reference before being initialized 
    self.myProperty <|= boxedProperty 
        ^

我可以確保編譯器我的assignment運算符函數總是初始化變量(也稱爲a.k.a.其操作數爲lhs)?另外,如果您有不同方法的想法,請隨時發表評論。


注:實際情況更爲複雜,需要的不僅僅是訪問value propterty一個Box結構的更「開箱」。

+0

您的問題不適合使用框和屬性的數組? init(boxedProperties:[Box ]){self.myProperties = boxedProperties.map {%0.value}}有了這個,20個或200個屬性,無論如何。 – GoZoner

+0

@GoZoner號現實生活中實際上是'init(anEnumValue:MyEnum)'。枚舉值包含不同的關聯值,它們需要雙「拆箱」。 – akashivskyy

+0

與其他Swift'賦值'操作符不同(除了實際賦值爲'='),您不需要'inout'參數 - 因爲如您所知,您從未實際引用該值。但是,在表面上你正在交易:'self.myProperty <| = boxedProperty'爲'self.myProperty = boxedProperty.value'。我不明白在這種情況下運營商的優勢,但我願意承認我不能看到你的實際問題。 – GoZoner

回答

3

由於賦值運算符使用表達式中的兩個操作數,所以無法對未初始化的變量執行操作,因此不能按照您所描述的方式執行此操作。那麼使用返回盒裝值的前綴運算符怎麼樣呢?

prefix operator <| { } 

prefix func <|<T>(rhs: Box<T>) -> T { 
    return rhs.value 
} 

// ... 

init(boxedProperty: Box<String>) { 
    self.myProperty = <|boxedProperty 
} 
+0

我也想過,使用中綴運算符似乎更自然(因爲我實際上正在開發一個開源框架)。 – akashivskyy

+0

在這種情況下,您唯一的方法是先給所有屬性默認值,然後使用中綴運算符來取消/處理。 –

+0

或讓它們可選。在這兩種情況下討厭的東西。 – akashivskyy

1

沒有理由使用語法來實現您的目標。只需定義一個函數,用'幾行'來封裝'switch聲明'。請確保功能與let約束,使其內init()是訪問這樣:

struct Box<T> { 
    let value: T 
    init (t:T) { 
    self.value = t 
    } 
}  

struct MyStruct { 
    let myProp: String 
    let myUnboxer : Box<String> -> String = { (b:Box<String>) in 
    /* switch, 5-10 lines */ 
    return b.value 
    } 

    init (bp:Box<String>) { 
    self.myProp = myUnboxer(bp) 
    } 
} 

在行動:

15> var ms = MyStruct(bp:Box(t:"abc")) 
ms: MyStruct = { 
    myProp = "abc" 
    myUnboxer = 
} 
16> ms.myProp 
$R0: String = "abc" 

的「成本」的,這是一個「額外」的實例變量myUnboxer但是當拆箱的細節變成特定結構時,這可能很快成爲優勢 - 此時,您還將使用拆箱器初始化MyStruct

對於我來說,在一個具有閉包和一流功能的語言中,發明語法通常是一個錯誤 - 除非有人正在主動定義子語言。具有特殊評估規則(又名「語法」)的陳述會混淆。

+0

它也有運營商作爲一流的功能。 ;)我認爲一個'assignment'運算符應該能夠「初始化」一個變量。 – akashivskyy

0

我已經使用枚舉來存儲對象的屬性,例如:

enum TextFieldProps { 
    case StringValue(String) 
    case Editable(Bool) 
} 

我然後有一個擴展的NSTextField包括稱爲assignProps函數,它的TextFieldProps數組:

func assignProps(tfp: [TextFieldProps]) { 
    tfp.reduce(self) { prop in 
     switch prop { 
      case .StringValue(let value): 
       self.stringValue = value 
      case .Editable(let value): 
       self.editable = value 
     } 
     return self 
    } 
} 

當我需要設置或更改NSTextField的屬性時,允許我只傳遞一個TextFieldPropsassign的數組,它使用reduce更新所有內容。

我認爲你正在嘗試使用類似的東西,只能用通用的Box來保存它裏面的值。也許有一種方法可以修改上述技巧以適合您的目的。如果不知道更多關於您正在使用的枚舉的結構和值,很難給出明確的答案。

我能說的是,如果你試圖設置20個屬性的值,那麼無論你是否使用自定義操作符,都會有20個賦值調用。

當我使用Box類型對象(我有時會這樣做),我有幾個不同的自定義函數。其中之一就被稱爲o,爲「打開」:

func o(b: Box<T>) -> T { 
    return b.value 
} 

// Now you can use it like this: 

init(b: Box<String>) { 
    self.property = o(b) 
} 

我知道這並不直接回答你問有關自定義賦值運算符的問題,但如果你縮短了初始化標籤b和使用函數名爲o,您已將整個事件降至4個字符,即o(b)。這對我來說簡短而高效,對我而言,o(b)直觀地看起來像是opened box的簡寫,所以代碼不會太混亂。

您可能希望限制o的範圍,以便框架的以後用戶可以使用o作爲變量名稱(如果他們需要)。實際上,你可以創建oMyStructinit功能是這樣的:

struct MyStruct { 
    let myProperty: String 

    init(b: Box<String>) { 
     let o: Box<String> -> String = { $0.value } 
     self.myProperty = o(b) 
    } 
} 

o現在是一個封閉的init函數中定義和範圍只限於該功能。您仍然可以使用o作爲您想要的代碼中任何其他地方的變量名稱,而不會產生任何衝突。

0

我認爲這可以通過使用默認屬性值來解決。例如。如:

struct Box<T> { 

    private let value: T? = nil 

    init(value: T) { 
     self.value = value 
    } 

} 

struct MyStruct { 

    let myProperty: String = "" 

    init(boxedProperty: Box<String>) { 
     self.myProperty <|= boxedProperty 
    } 

} 

infix operator <|= { associativity right precedence 90 assignment } 

func <|= <T>(inout lhs: T, rhs: Box<T>) { 
    lhs = rhs.value! 
} 

var box = Box<String>(value: "foo") 

var myStruct = MyStruct(boxedProperty: box) 
+0

是的,但屬性不應該是可選的。 – akashivskyy

相關問題