DuoCode 和 Knockout
DuoCode and Knockout
我正在考虑 DuoCode 作为 TypeScript 的替代品,因为它使用 C#,这意味着我的开发人员可以使用他们已有的 C# 知识,而且我们还可以在客户端和客户端上重用验证逻辑等服务器。
目前没有针对 Knockout 的绑定,所以我自己创建了一个,非常简单
namespace Knockout
{
[Js(Name = "ko", Extern = true)]
public static class Global
{
[Js(Name = "observable", OmitGenericArgs = true)]
public static extern Observable<T> Observable<T>();
[Js(Name = "observable", OmitGenericArgs = true)]
public static extern Observable<T> Observable<T>(T value);
[Js(Name = "computed", OmitGenericArgs = true)]
public static extern Observable<T> Computed<T>(Func<T> computed);
[Js(Name = "applyBindings")]
public static extern void ApplyBindings(object viewModel);
[Js(Name = "unwrap", OmitGenericArgs = true)]
public static extern T Unwrap<T>(Observable<T> observable);
}
[Js(Name = "ko.observable", Extern = true)]
public class Observable<T>
{
//TODO: Add more methods like subscribe, extend etc
}
}
这是一个使用它的简单模型
namespace ViewModels
{
public class FooViewModel
{
public FooViewModel()
{
Bar = Global.Observable("HelloWorld");
Computed = Global.Computed(() => Global.Unwrap(Bar) + "COMPUTED");
}
public Observable<string> Bar { get; set; }
public Observable<string> Computed { get; set; }
}
}
Computed func 可以使用 Global.Unwrap
的底层可观察值,在客户端上转换为 ko.unwap
但是对于设置值我还没有想出一个可靠的解决方案,我找到的唯一解决方案是
Js.referenceAs<Action<string>>("this.Bar")("New Value");
有许多缺点成为公认的解决方案
有什么想法吗?
编辑:扩展方法让它变得更好一些,但缺点是您现在需要包含使用 Knockout 绑定 class 库编译的 javascript 库
public static class ObservableExtensions
{
public static void SetValue<T>(this Observable<T> observable, T value)
{
Js.referenceAs<Action<T>>("observable")(value);
}
}
对 Yoav 回答的思考
- 优于 referenceAs,但过于冗长且类型安全性太差
- JsFunction 与上面基本相同
- 当时看来是最好的解决方案
我现在有了这个
public static class ObservableExtensions
{
public static void Set<T>(this Observable<T> observable, T value)
{
observable.As<Action<T>>()(value);
}
public static T Get<T>(this Observable<T> observable)
{
return observable.As<Func<T>>()();
}
}
有点遗憾,因为它引入了一个不需要的额外函数调用
Knockout.ObservableExtensions.Set(String, this.get_Bar(), "New value");
而不只是
this.get_Bar()("New value");
我还有一个问题,我也在看knockout Observable Arrays,我有这个
[Js(Name = "ko.observableArray", Extern = true)]
public class ObservableArray<T> : Observable<JsArray<T>>
{
[Js(Name = "push", OmitGenericArgs = true)]
public extern void Push(T value);
}
在我的全局 KO 静态 class 我有
[Js(Name = "observableArray", OmitGenericArgs = true)]
public static extern ObservableArray<T> ObservableArray<T>(T[] values);
我已经为方法参数尝试了不同的值,如 JsArray、IEnumerable 等,它们都在客户端上生成相同的代码
ko.observableArray($d.array(System.Int32, [1, 2, 3, 4]));
这将失败,因为 Knockout 可观察数组需要一个正常的 Javascript 数组。
我会推荐以下内容:
- 而不是使用 Js.referenceAs 你可以像这样使用 As 扩展方法:
Bar.As<Action<string>>()("New Value");
- 另一种选择是让Observable继承自JsFunction,然后就可以调用invoke 像这样:
Bar.invoke("New Value");
另一个选项是在扩展方法中使用相同的方法,类似于您所建议的。像这样定义一个方法:
public static void Set<T>(this Observable<T> o, T value)
{
o.As<Action<T>>()(value);
}
(注意必须是扩展方法因为Observableclass是extern的,所以在不同的static中定义这个方法class)
然后你可以像这样使用它:Bar.Set("New value");
此外,我建议将 Bar 设为字段而不是 属性(它将生成更清晰的代码)
(披露:我与 DuoCode 开发人员一起工作)
编辑
我同意,扩展方法是目前最好的选择。也许在不久的将来他们会支持这样的东西:
[Js(Name="")]
public void SetValue(T value)
关于你的第二个问题:查看 mscorlib.js 中的代码,$d.array
创建普通数组(只是有一些额外的运行时类型信息属性)。这里的问题是它为 Int32 这样的类型创建了 typed-arrays。所以我想你可以创建一个 object 数组而不是 T 并且它应该可以工作,就像这样:
[Js(Name = "observableArray", OmitGenericArgs = true)]
public static extern ObservableArray<T> ObservableArray<T>(object[] values);
我正在考虑 DuoCode 作为 TypeScript 的替代品,因为它使用 C#,这意味着我的开发人员可以使用他们已有的 C# 知识,而且我们还可以在客户端和客户端上重用验证逻辑等服务器。
目前没有针对 Knockout 的绑定,所以我自己创建了一个,非常简单
namespace Knockout
{
[Js(Name = "ko", Extern = true)]
public static class Global
{
[Js(Name = "observable", OmitGenericArgs = true)]
public static extern Observable<T> Observable<T>();
[Js(Name = "observable", OmitGenericArgs = true)]
public static extern Observable<T> Observable<T>(T value);
[Js(Name = "computed", OmitGenericArgs = true)]
public static extern Observable<T> Computed<T>(Func<T> computed);
[Js(Name = "applyBindings")]
public static extern void ApplyBindings(object viewModel);
[Js(Name = "unwrap", OmitGenericArgs = true)]
public static extern T Unwrap<T>(Observable<T> observable);
}
[Js(Name = "ko.observable", Extern = true)]
public class Observable<T>
{
//TODO: Add more methods like subscribe, extend etc
}
}
这是一个使用它的简单模型
namespace ViewModels
{
public class FooViewModel
{
public FooViewModel()
{
Bar = Global.Observable("HelloWorld");
Computed = Global.Computed(() => Global.Unwrap(Bar) + "COMPUTED");
}
public Observable<string> Bar { get; set; }
public Observable<string> Computed { get; set; }
}
}
Computed func 可以使用 Global.Unwrap
的底层可观察值,在客户端上转换为 ko.unwap
但是对于设置值我还没有想出一个可靠的解决方案,我找到的唯一解决方案是
Js.referenceAs<Action<string>>("this.Bar")("New Value");
有许多缺点成为公认的解决方案
有什么想法吗?
编辑:扩展方法让它变得更好一些,但缺点是您现在需要包含使用 Knockout 绑定 class 库编译的 javascript 库
public static class ObservableExtensions
{
public static void SetValue<T>(this Observable<T> observable, T value)
{
Js.referenceAs<Action<T>>("observable")(value);
}
}
对 Yoav 回答的思考
- 优于 referenceAs,但过于冗长且类型安全性太差
- JsFunction 与上面基本相同
- 当时看来是最好的解决方案
我现在有了这个
public static class ObservableExtensions
{
public static void Set<T>(this Observable<T> observable, T value)
{
observable.As<Action<T>>()(value);
}
public static T Get<T>(this Observable<T> observable)
{
return observable.As<Func<T>>()();
}
}
有点遗憾,因为它引入了一个不需要的额外函数调用
Knockout.ObservableExtensions.Set(String, this.get_Bar(), "New value");
而不只是
this.get_Bar()("New value");
我还有一个问题,我也在看knockout Observable Arrays,我有这个
[Js(Name = "ko.observableArray", Extern = true)]
public class ObservableArray<T> : Observable<JsArray<T>>
{
[Js(Name = "push", OmitGenericArgs = true)]
public extern void Push(T value);
}
在我的全局 KO 静态 class 我有
[Js(Name = "observableArray", OmitGenericArgs = true)]
public static extern ObservableArray<T> ObservableArray<T>(T[] values);
我已经为方法参数尝试了不同的值,如 JsArray、IEnumerable 等,它们都在客户端上生成相同的代码
ko.observableArray($d.array(System.Int32, [1, 2, 3, 4]));
这将失败,因为 Knockout 可观察数组需要一个正常的 Javascript 数组。
我会推荐以下内容:
- 而不是使用 Js.referenceAs 你可以像这样使用 As 扩展方法:
Bar.As<Action<string>>()("New Value");
- 另一种选择是让Observable继承自JsFunction,然后就可以调用invoke 像这样:
Bar.invoke("New Value");
另一个选项是在扩展方法中使用相同的方法,类似于您所建议的。像这样定义一个方法:
public static void Set<T>(this Observable<T> o, T value) { o.As<Action<T>>()(value); }
(注意必须是扩展方法因为Observableclass是extern的,所以在不同的static中定义这个方法class) 然后你可以像这样使用它:
Bar.Set("New value");
此外,我建议将 Bar 设为字段而不是 属性(它将生成更清晰的代码)
(披露:我与 DuoCode 开发人员一起工作)
编辑
我同意,扩展方法是目前最好的选择。也许在不久的将来他们会支持这样的东西:
[Js(Name="")]
public void SetValue(T value)
关于你的第二个问题:查看 mscorlib.js 中的代码,$d.array
创建普通数组(只是有一些额外的运行时类型信息属性)。这里的问题是它为 Int32 这样的类型创建了 typed-arrays。所以我想你可以创建一个 object 数组而不是 T 并且它应该可以工作,就像这样:
[Js(Name = "observableArray", OmitGenericArgs = true)]
public static extern ObservableArray<T> ObservableArray<T>(object[] values);