Как наследовать от объекта CSLA в F#?

Я хотел бы получить преимущества CSLA от F#, но у меня проблемы с наследованием. Вот класс ProjectTracker ResourceInfo. Может кто-нибудь показать, пожалуйста, как это сделать в F#?

using Csla;
using System;
using Csla.Serialization;

namespace ProjectTracker.Library
{
  [Serializable()]
  public class ResourceInfo : ReadOnlyBase<ResourceInfo>
  {
    private static PropertyInfo<int> IdProperty = RegisterProperty<int>(c => c.Id);
    public int Id
    {
      get { return GetProperty(IdProperty); }
      private set { LoadProperty(IdProperty, value); }
    }

    private static PropertyInfo<string> NameProperty = RegisterProperty<string>(c => c.Name);
    public string Name
    {
      get { return GetProperty(NameProperty); }
      private set { LoadProperty(NameProperty, value); }
    }

    public override string ToString()
    {
      return Name;
    }

    internal ResourceInfo(int id, string lastname, string firstname)
    {
      Id = id;
      Name = string.Format("{0}, {1}", lastname, firstname);
    }
  }
}

3 ответа

Решение от jpalmer показывает общую структуру, но я думаю, что есть несколько проблем. У меня нет опыта работы с CSLA, поэтому я не пробовал запускать это, но я скачал DLL и попытался проверить тип в образце.

Прежде всего, RegisterProperty Метод принимает не лямбда-функцию, а выражение (и использует ее для получения информации о свойстве с помощью отражения). Чтобы это работало, вам нужно написать помощник, используя цитаты F#:

open Microsoft.FSharp.Quotations
open System.Linq.Expressions

let prop (q:Expr<'T -> 'R>) = 
  match q with
  | Patterns.Lambda(v, Patterns.PropertyGet(_, pi, _)) -> 
      let v = Expression.Variable(v.Type)
      Expression.Lambda<Func<'T, 'R>>
        (Expression.Property(v, pi), [v])
  | _ -> failwith "wrong quotation"

Это преобразует лямбда-функцию F# в кавычки в дерево выражений C# в ожидаемом формате. Вы можете позвонить RegisterProperty с чем-то вроде prop <@ fun (a:Foo) -> a.Bar @> в качестве аргумента.

Я тоже вижу что IdProperty должно быть статичным, что можно сделать с помощью static let (если это личное). Следующее должно быть правильным способом определения типа с одним свойством:

[<Serializable>]
type ResourceInfo internal (id:int, lastname:string, firstname:string) as this =
  inherit ReadOnlyBase<ResourceInfo>()

  // Code executed as part of the constructor    
  do this.Id <- id

  static let IdProperty = 
    ReadOnlyBase<ResourceInfo>.RegisterProperty<int>
      (prop <@ fun (r:ResourceInfo) -> r.Id @>)

  member x.Id 
    with get() = x.GetProperty(IdProperty) |> unbox
    and set(v) = x.LoadProperty(IdProperty, v)

Мне вообще очень нравится стиль, когда вы пишете модификаторы доступности прямо в вашем коде (как в C#), поэтому я аннотировал конструктор с помощью internal как в вашем коде. Я также добавил тело конструктора, который устанавливает Id свойство при создании объекта.

@Tomas Я польщен твоим ответом и тронут твоей попыткой сделать это - загрузкой CSLA, определением выражения как проблемы и созданием неочевидного способа борьбы с ним. Мне нравится ваша книга "Функциональное программирование в реальном мире", которая выходит за рамки языковых возможностей и того, как применять их для решения важных проблем в реальном мире.

CSLA отсутствовал до того, как в C# были лямбды, поэтому я вернулся, чтобы посмотреть, как Lhotka тогда использовал RegisterProperty. Если другие пользователи хотят избегать выражений, похоже, это тоже работает:

static let IdProperty =
  ReadOnlyBase<ResourceInfo>.RegisterProperty
    (typeof<ResourceInfo>, new PropertyInfo<int>("Id"))

Это должно быть близко - стандартный способ сделать контроль доступа в F# - это использовать файлы сигнатур, которые я пропустил

module ProjectTracker.Library
open Csla;
open System;
open Csla.Serialization;

  [<Serializable>]
  type  ResourceInfo(id, lastname, firstname) =
    inherit ReadOnlyBase<ResourceInfo>()
    Id <- id
    Name <- sprintf "%s, %s" lastname firstname
    let IdProperty = RegisterProperty<int>(fun c -> c.Id); 
    member x.Id with get() = GetProperty(IdProperty) and set(v) = LoadProperty(IdProperty, v)

   //skipped a property here - similar to above
    override x.ToString() = Name
Другие вопросы по тегам