F#,将受歧视的联合序列化为缺少数据的值

时间:2011-12-19 15:39:39

标签: wcf serialization f#

首先,我必须说我知道在与.NET中的其他语言集成时使用F#特定的东西通常不是一个好主意。

我的问题是我不明白如何创建服务引用到包含暴露歧视联合的方法的服务。

我得到的基础知识有点像这样:

  type TelephonyProductActivationData =
  | MobileUseNextIcc 
  | Mobile of decimal
  | MobileBroadbandUseNextIcc
  | MobileBroadband of decimal
  | Fixed
  | Voip of int16 * int16
  static member KnownTypes() =
      typeof<TelephonyProductActivationData>.GetNestedTypes(BindingFlags.Public ||| BindingFlags.NonPublic) |> Array.filter FSharpType.IsUnion

如果您使用F#interactive首先创建类型:

type TelephonyProductActivationData =
  | MobileUseNextIcc of unit
  | Mobile of decimal<Icc>
  | MobileBroadbandUseNextIcc of unit
  | MobileBroadband of decimal<Icc>
  | Fixed of unit
  | Voip of BoxNr * int16<BoxPort>;;

然后执行知识类型代码部分(稍加修改):

(typeof<TelephonyProductActivationData>.GetNestedTypes(System.Reflection.BindingFlags.Public ||| System.Reflection.BindingFlags.NonPublic) |> Array.filter Microsoft.FSharp.Reflection.FSharpType.IsUnion) |> Array.map (fun x -> x.FullName);;

您将看到以下输出:

val it : string [] =
  [|"FSI_0047+TelephonyProductActivationData+Mobile";
    "FSI_0047+TelephonyProductActivationData+MobileBroadband";
    "FSI_0047+TelephonyProductActivationData+Voip"|]

请注意,没有与之关联的数据的值消失了。这意味着在编译这个有区别的联合时不会创建任何类型。通过在F#interactive中执行此语句:

typeof<TelephonyProductActivationData>.GetProperties() |> Array.map (fun x -> (x.Name));;

我们将看到它们已成为:

val it : string [] =
  [|"Tag"; "IsVoip"; "Fixed"; "IsFixed"; "IsMobileBroadband";
    "MobileBroadbandUseNextIcc"; "IsMobileBroadbandUseNextIcc"; "IsMobile";
    "MobileUseNextIcc"; "IsMobileUseNextIcc"|]

正如您所看到的,没有与之关联的数据的值已成为属性。现在我可以告诉你真正的问题。使用此方法创建服务的服务引用时,我得到的是:

[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "4.0.0.0")]
[System.Runtime.Serialization.DataContractAttribute(Name="ActivationModel.TelephonyProductActivationData", Namespace="http://schemas.datacontract.org/2004/07/Svea.Inri.Data")]
[System.SerializableAttribute()]
[System.Runtime.Serialization.KnownTypeAttribute(typeof(ConsoleApplication1.ServiceReference1.ActivationModelTelephonyProductActivationData.Mobile))]
[System.Runtime.Serialization.KnownTypeAttribute(typeof(ConsoleApplication1.ServiceReference1.ActivationModelTelephonyProductActivationData.MobileBroadband))]
[System.Runtime.Serialization.KnownTypeAttribute(typeof(ConsoleApplication1.ServiceReference1.ActivationModelTelephonyProductActivationData.Voip))]
public partial class ActivationModelTelephonyProductActivationData : object, System.Runtime.Serialization.IExtensibleDataObject, System.ComponentModel.INotifyPropertyChanged {

    [System.NonSerializedAttribute()]
    private System.Runtime.Serialization.ExtensionDataObject extensionDataField;

    private int _tagField;

    [global::System.ComponentModel.BrowsableAttribute(false)]
    public System.Runtime.Serialization.ExtensionDataObject ExtensionData {
        get {
            return this.extensionDataField;
        }
        set {
            this.extensionDataField = value;
        }
    }

    [System.Runtime.Serialization.DataMemberAttribute(IsRequired=true)]
    public int _tag {
        get {
            return this._tagField;
        }
        set {
            if ((this._tagField.Equals(value) != true)) {
                this._tagField = value;
                this.RaisePropertyChanged("_tag");
            }
        }
    }

    public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;

    protected void RaisePropertyChanged(string propertyName) {
        System.ComponentModel.PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
        if ((propertyChanged != null)) {
            propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
        }
    }

    [System.Diagnostics.DebuggerStepThroughAttribute()]
    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "4.0.0.0")]
    [System.Runtime.Serialization.DataContractAttribute(Name="ActivationModel.TelephonyProductActivationData.Mobile", Namespace="http://schemas.datacontract.org/2004/07/Svea.Inri.Data")]
    [System.SerializableAttribute()]
    public partial class Mobile : ConsoleApplication1.ServiceReference1.ActivationModelTelephonyProductActivationData {

        private decimal itemField;

        [System.Runtime.Serialization.DataMemberAttribute(IsRequired=true)]
        public decimal item {
            get {
                return this.itemField;
            }
            set {
                if ((this.itemField.Equals(value) != true)) {
                    this.itemField = value;
                    this.RaisePropertyChanged("item");
                }
            }
        }
    }

    [System.Diagnostics.DebuggerStepThroughAttribute()]
    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "4.0.0.0")]
    [System.Runtime.Serialization.DataContractAttribute(Name="ActivationModel.TelephonyProductActivationData.MobileBroadband", Namespace="http://schemas.datacontract.org/2004/07/Svea.Inri.Data")]
    [System.SerializableAttribute()]
    public partial class MobileBroadband : ConsoleApplication1.ServiceReference1.ActivationModelTelephonyProductActivationData {

        private decimal itemField;

        [System.Runtime.Serialization.DataMemberAttribute(IsRequired=true)]
        public decimal item {
            get {
                return this.itemField;
            }
            set {
                if ((this.itemField.Equals(value) != true)) {
                    this.itemField = value;
                    this.RaisePropertyChanged("item");
                }
            }
        }
    }

    [System.Diagnostics.DebuggerStepThroughAttribute()]
    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "4.0.0.0")]
    [System.Runtime.Serialization.DataContractAttribute(Name="ActivationModel.TelephonyProductActivationData.Voip", Namespace="http://schemas.datacontract.org/2004/07/Svea.Inri.Data")]
    [System.SerializableAttribute()]
    public partial class Voip : ConsoleApplication1.ServiceReference1.ActivationModelTelephonyProductActivationData {

        private string item1Field;

        private short item2Field;

        [System.Runtime.Serialization.DataMemberAttribute(IsRequired=true)]
        public string item1 {
            get {
                return this.item1Field;
            }
            set {
                if ((object.ReferenceEquals(this.item1Field, value) != true)) {
                    this.item1Field = value;
                    this.RaisePropertyChanged("item1");
                }
            }
        }

        [System.Runtime.Serialization.DataMemberAttribute(IsRequired=true)]
        public short item2 {
            get {
                return this.item2Field;
            }
            set {
                if ((this.item2Field.Equals(value) != true)) {
                    this.item2Field = value;
                    this.RaisePropertyChanged("item2");
                }
            }
        }
    }
}

ActivationModelTelephonyProductActivationData没有子类(ActivationModel部分是命名空间),它表示没有任何数据的值,并且在基类中没有属性可以设置没有任何数据的值。

我的问题终于来了,一个人应该如何做到这一点。我是否必须将“of unit”添加到我没有数据的所有受歧视的联合值中。

2 个答案:

答案 0 :(得分:3)

如果您定义如下所示的DU类型,它将起作用。

[<KnownType("KnownTypes")>]
//[<DataContract>] // note: keep KnownTypes, but avoid DataContract 
//  so that DataContractSerializer uses .NET 'Serializable' instead
type TelephonyProductActivationData = 
  | MobileUseNextIcc
  | Mobile of decimal 
  | MobileBroadbandUseNextIcc
  | MobileBroadband of decimal 
  | Fixed
  | Voip of int16 * int16 
  static member KnownTypes() = 
      typeof<TelephonyProductActivationData>.GetNestedTypes(BindingFlags.Public |||
                                                            BindingFlags.NonPublic) 
      |> Array.filter FSharpType.IsUnion 

答案 1 :(得分:1)

您实际上依赖于实现细节(DUs的编译形式)来实现此目的。即使将每个案例改为非虚空也会让我感到厌恶。我认为理想的解决方案是使用类。 DU大致对应于DU类型的抽象基类和每种情况的子类。您可以自己创建类型层次结构,获得类似的效果,并获得更好的结果。

编辑:编译后的DU形式,而实现细节 defined in the spec因此不太可能改变。但是,自己布置类型会使其明确,并且可以防止您必须解决这些问题。