提供自己的 setter 是不好的做法还是我应该使用 setproperty ?

5 julia

假设我有以下内容Employee struct

mutable struct Employee
  _id::Int64
  _first_name::String
  _last_name::String
  
  function Employee(_id::Int64,_first_name::String,_last_name::String)
    # validation left out.
    new(_id,_first_name,_last_name)
  end
end
Run Code Online (Sandbox Code Playgroud)

如果我想实现我自己的,setproperty!()我可以这样做:

function setproperty!(value::Employee,name::Symbol,x)
  if name == :_id
    if !isa(x,Int64)
      throw(ErrorException("ID type is invalid"))
    end
    setfield!(value,:_id,x)
  end
  if name == :_first_name
    if is_white_space(x)
      throw(ErrorException("First Name cannot be blank!"))
    end
    setfield!(value,:_first_name,x) 
  end
  if name == :_last_name
    if is_white_space(x)
      throw(ErrorException("Last Name cannot be blank!"))
    end 
    setfield!(value,:_last_name,x)
  end 
end
Run Code Online (Sandbox Code Playgroud)

我执行setproperty!()正确了吗?

setfield!()我使用for_first_name和,的原因_last_name是因为如果我这样做:

if name == :_first_name
  setproperty!(value,:_first_name,x) # or value._first_name = x 
end 
Run Code Online (Sandbox Code Playgroud)

它会导致 aStackOverflowError因为它递归地使用setproperty!().

我真的不喜欢使用setproperty!(),因为随着参数数量的增加, 也会增加setproperty!()

它还让人想起使用Enum 和 if 语句(只有我们用 切换EnumSymbol

我喜欢的一种解决方法是记录字段的含义private并使用提供的setter来设置字段:

function set_first_name(obj::Employee,first_name::AbstractString)
    # Validate first_name before assigning it.   
    obj._first_name = first_name
end
Run Code Online (Sandbox Code Playgroud)

该函数较小且用途单一。

当然,这并不能阻止某人使用setproperty!(),setfield!()value._field_name = x,但如果您要规避提供的内容setter,那么您将需要承担这样做的后果。

lon*_*000 2

当然,这并不能阻止某人使用 setproperty!()、setfield!() 或 value._field_name = x,但如果您要规避提供的 setter,那么您将需要承担这样做的后果。

我建议您这样做,定义 getter、setter 函数,而不是重载getproperty/setproperty!. 在野外,我看到的重载的主要用途getproperty/setproperty!是可以根据数据计算字段。对于 getter/setter 模式,我建议您使用约定!: getter:

function first_name(value::Employee)
  return value._first_name
end
Run Code Online (Sandbox Code Playgroud)

设置者:

function first_name!(value::Employee,text::String)
  #validate here 
  value._first_name = text
  return value._first_name 
end
Run Code Online (Sandbox Code Playgroud)

如果您的结构是可变的,则可能某些字段未初始化。您可以通过添加一个方法来添加默认的 getter:

function first_name(value::Employee,default::String)
  value_stored = value._first_name
  if is_initialized(value_stored) #define is_initialized function
    return value_stored
  else
    return default
  end
end
Run Code Online (Sandbox Code Playgroud)

使用默认的 setter/getter 时,first_name(val,text)和之间的唯一区别first_name!(val,text)是 的可变性val,但结果是相同的。如果您正在执行可变函数与不可变函数,则很有用。正如你所说,getproperty/setproperty!相比之下,这很麻烦。如果您想禁止访问这些字段,您可以这样做:

Base.getproperty(val::Employee,key::Symbol) = throw(error("use the getter functions instead!")

Base.setproperty!(val::Employee,key::Symbol,x) = throw(error("use the setter functions instead!")
Run Code Online (Sandbox Code Playgroud)

val.key禁止使用and的语法糖val.key = x。(如果有人真的想要原始访问,仍然有getfield/setfield!,但他们被警告了。)

最后,我在 julia 文档中找到了这个建议,建议使用 getter/setter 方法而不是直接字段访问 https://docs.julialang.org/en/v1/manual/style-guide/#Prefer-exported-methods-over-直接现场访问