Как наследовать от объекта 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