2015-05-26 14 views
6

Tôi có một lớp với các thuộc tính có nhiều tác dụng phụ kích hoạt khi các trình định vị của nó được gọi, chẳng hạn như thay đổi các sự kiện được kích hoạt và/hoặc các hành động hiển thị. Làm thế nào tôi có thể thiết kế một chức năng mà sẽ thiết lập các tài sản chỉ khi giá trị của nó cần phải thay đổi?F # - Đặt thuộc tính nếu đã thay đổi

Với refs, tôi sẽ chỉ làm điều gì đó như thế này:

let setIfChanged (oldVal: Ref<'T>) (newVal: 'T) = if newVal <> !oldVal then oldVal := newVal 
let y = ref 0 
setIfChanged y 1 

Tuy nhiên, tài sản có thể thay đổi không refs, và tôi dường như không thể tìm thấy bất cứ cách nào để thiết kế một điều như vậy mà sẽ biên dịch. Ví dụ:

let setIfChanged oldVal newVal = if newVal <> oldVal then oldVal <- newVal 

sẽ chỉ cho tôi biết rằng oldVal không thể thay đổi được.

Có cách nào để chú thích oldVal sao cho nó dành cho các thuộc tính có thể thay đổi (có thể đặt)? đây có phải là cách tốt hơn không?

+0

cũng thật xấu hổ (thông thường bạn sẽ muốn setter được thông minh để không làm tác dụng phụ khi không có gì thay đổi) bạn không thể thay đổi nguồn - cá nhân tôi sẽ xem xét gói * vi phạm * loại - câu trả lời Tomas có vẻ là một cách hay để làm điều này * shortish * – Carsten

Trả lời

4

Bạn có thể làm:

let setIfChanged (oldVal: 'a byref) (newVal : 'a) = if newVal <> oldVal then oldVal <- newVal 
let mutable test = 42 
setIfChanged &test 24 

đó đang được nói, được đưa ra lời giải thích của bạn trong những mục tiêu, tôi sẽ khuyên bạn nên nhìn vào Gjallarhorn. Nó cung cấp hỗ trợ cho tín hiệu trên đầu trang của các giá trị có thể thay đổi và được thiết kế cực kỳ linh hoạt về những gì xảy ra khi giá trị thay đổi. Nó có thể được sử dụng như một cơ chế cơ bản mà bạn có thể dễ dàng xây dựng tín hiệu của bạn khi mọi thứ thay đổi (nếu nó không cung cấp những gì bạn cần thông qua mô-đun View).


Edit:

Với nhận xét mà bạn sở hữu trong câu hỏi nằm trong một hội đồng của bên thứ ba, một lựa chọn sẽ được sử dụng sự hỗ trợ năng động trong F # để ghi đè lên op_DynamicAssignment điều hành (?<-) để tự động cử với phương thức này, nhưng hãy nâng "thông báo" của bạn lên trong quá trình.

Given:

let (?<-) source property (value : 'a) = 
    let p = source.GetType().GetProperty(property) 
    let r = p.GetValue(source, null) :?> 'a 
    if (r <> value) then 
     printfn "Changing value" 
     source.GetType().GetProperty(property).SetValue(source, value, null) 

Bạn có thể gọi someObj?TheProperty <- newVal, và bạn sẽ thấy "giá trị đang thay đổi" chỉ in nếu giá trị đã thực sự thay đổi.

Nội bộ, tính năng này hoạt động bằng cách sử dụng phản chiếu để lấy giá trị cũ của thuộc tính và đặt giá trị mới, vì vậy nó sẽ không có đặc tính hiệu suất tốt nhất, nhưng khi bạn sử dụng đó có thể không phải là một vấn đề.

+0

Làm cách nào để làm việc với một thuộc tính đối tượng? ví dụ: 'setIfChanged foo.Text" bar "'. Ngoài ra, tôi không có quyền kiểm soát các lớp và các thành viên của chúng, được định nghĩa trong một thư viện C# bên ngoài. – dannytoone

+1

@ dannytoone Ahh, phải làm việc với một lĩnh vực. Nó sẽ không làm việc chống lại một tài sản ... đã không nhận ra bạn không có quyền truy cập vào các loại. –

+1

@dannytoone Đã cho bạn một sự thay thế bằng cách sử dụng hỗ trợ động trong F # để "bao bọc" quyền truy cập, cho phép bạn thực hiện thao tác của mình trong quá trình này. –

5

Tôi không nghĩ có cách nào dễ dàng để làm điều này "miễn phí".

Một mẹo nhỏ mà có thể làm điều này một chút dễ dàng hơn là bạn thực sự có thể đối xử với các phương thức getter và setter tài sản như các chức năng, vì vậy bạn có thể viết setIfChanged như một hàm dùng hai chức năng:

let setIfChanged getter setter v = 
    if getter() <> v then setter(v) 

Cho một lớp A với proeperty P, sau đó bạn có thể gọi đây là sử dụng set_Pget_P như các thông số:

let a = A() 
setIfChanged a.get_P a.set_P 0 
setIfChanged a.get_P a.set_P 1 

thực tế là trình biên dịch cho phép bạn truy cập get_Pset_P là một mẹo nhỏ được biết đến - vì vậy có thể hơi khó hiểu đối với nhiều người. Để hoàn chỉnh, đây là định nghĩa của A mà tôi sử dụng để thử nghiệm:

type A() = 
    let mutable p = 0 
    member x.P 
    with get() = p 
    and set(v) = printfn "Setting!"; p <- v 

Một cách tiếp cận phức tạp hơn sẽ được sử dụng trích dẫn và phản ánh, đó sẽ là chậm hơn, nhưng nó sẽ cho phép bạn viết một cái gì đó giống như setIfChanged <@ a.P @> 42.

Các vấn đề liên quan