我最近在C#中将SOLID做到了非常极端的水平,并且在某些时候意识到我现在基本上没有做太多的事情.在我最近再次开始研究F#之后,我认为对于我现在正在做的很多事情,它可能是更合适的语言选择,所以我想尝试将一个真实的C#项目移植到F#作为概念的证明.我想我可以推出实际的代码(以一种非惯用的方式),但我无法想象一个架构会是什么样的,它允许我以与C#类似的灵活方式工作.
我的意思是我有很多小类和接口,我使用IoC容器编写,我也使用了像Decorator和Composite这样的模式.这导致(在我看来)非常灵活和可演化的整体架构,使我能够在应用程序的任何位置轻松替换或扩展功能.根据所需更改的大小,我可能只需要编写一个新的接口实现,在IoC注册中替换它并完成.即使更改更大,我也可以替换部分对象图,而应用程序的其余部分只是像以前一样.
现在使用F#,我没有类和接口(我知道我可以,但我认为这与我想要进行实际函数式编程的时间不同),我没有构造函数注入,而且我没有IoC容器.我知道我可以使用更高阶函数做类似Decorator模式的东西,但这似乎并没有给我带有构造函数注入的类的灵活性和可维护性.
考虑这些C#类型:
public class Dings
{
public string Lol { get; set; }
public string Rofl { get; set; }
}
public interface IGetStuff
{
IEnumerable<Dings> For(Guid id);
}
public class AsdFilteringGetStuff : IGetStuff
{
private readonly IGetStuff _innerGetStuff;
public AsdFilteringGetStuff(IGetStuff innerGetStuff)
{
this._innerGetStuff = innerGetStuff;
}
public IEnumerable<Dings> For(Guid id)
{
return this._innerGetStuff.For(id).Where(d => d.Lol == "asd");
}
}
public class GeneratingGetStuff : IGetStuff
{
public IEnumerable<Dings> For(Guid id)
{
IEnumerable<Dings> dingse;
// somehow knows how to …
Run Code Online (Sandbox Code Playgroud) 我正在使用XUnit框架来测试我的C#代码.
这个框架中是否有可用于对象比较的断言方法?我的目的是检查每个对象的公共和私有成员变量是否相等.
我试过那些替代品但很少有效:
1) bool IsEqual = (Obj1 == Obj2)
2) Assert.Same(Obj1, Obj2) which I couldnt understand what happens internally
Run Code Online (Sandbox Code Playgroud) 我正在构建一个WinForms应用程序,其UI只包含一个NotifyIcon
动态填充的UI ContextMenuStrip
.有一个MainForm
共同持有的申请,但这是从来没有看见.
我开始尽可能安全地构建它(使用Autofac来处理对象图)并且对我的成功感到非常满意,即使使用O部分也相当不错.随着我目前正在实施的扩展,似乎我发现了我的设计中的一个缺陷,需要重新改造一下; 我想知道我需要去的方式但是有点不清楚如何准确定义依赖关系.
如上所述,菜单部分在启动应用程序后动态填充.为此,我定义了一个IToolStripPopulator
接口:
public interface IToolStripPopulator
{
System.Windows.Forms.ToolStrip PopulateToolStrip(System.Windows.Forms.ToolStrip toolstrip, EventHandler itemclick);
}
Run Code Online (Sandbox Code Playgroud)
将此实现注入到MainForm
,并且Load()
方法PopulateToolStrip()
使用ContextMenuStrip
表单中定义的处理程序调用.populator的依赖关系仅与获取用于菜单项的数据有关.
这个抽象通过一些进化步骤很好地工作,但是当我需要多个事件处理程序时,这已经不够了,例如因为我正在创建几个不同的菜单项组 - 仍然隐藏在单个IToolStripPopulator
界面后面,因为表单不应该是完全关心这一点.
正如我所说,我想我知道一般结构应该是什么样的 - 我将IToolStripPopulator
接口重命名为更具体的东西*并创建了一个新PopulateToolStrip()
方法,其方法不接受EventHandler
参数,而是将其注入到对象中(也允许更多关于实现所需的处理程序数量的更多灵活性等).这样,我的"最重要的" IToolStripPopulator
可以很容易地成为任何数量的特定适配器的适配器.
现在我不清楚的是我应该如何解决EventHandler依赖项.我认为处理程序应该全部定义MainForm
,因为它具有正确响应菜单事件所需的所有其他依赖项,并且它还"拥有"菜单.这意味着我IToolStripPopulator
最终注入到MainForm中的MainForm
对象的依赖关系需要依赖于对象本身Lazy<T>
.
我的第一个想法是定义一个IClickHandlerSource
接口:
public interface IClickHandlerSource
{
EventHandler GetClickHandler();
}
Run Code Online (Sandbox Code Playgroud)
这是由我实现的MainForm
,我的具体IToolStripPopulator
实现依赖于Lazy<IClickHandlerSource>
.虽然这有效,但它是不灵活的.我要么必须为可能越来越多的处理程序定义单独的接口(严重违反OCP与MainForm
类)或不断扩展IClickHandlerSource
(主要违反ISP).直接依赖事件处理程序在消费者方面看起来是一个好主意,但是通过惰性实例(或类似)的属性单独连接构造函数似乎非常混乱 - 如果可能的话.
我最好的打赌似乎是这样的: …
c# dependency-injection inversion-of-control winforms solid-principles
现在找到解决方案的简短内容:
AutoFixture返回冻结模拟就好了; 我的sut也是由AutoFixture生成的,只有一个公共属性,其本地默认值对于测试非常重要,并且AutoFixture设置为新值.除了Mark的答案之外,还有很多值得学习的东西.
原始问题:
我昨天开始尝试使用AutoFixture进行我的xUnit.net测试,这些测试中包含了Moq.我希望更换一些Moq的东西或者让它更容易阅读,我特别感兴趣的是在SUT工厂容量中使用AutoFixture.
我使用Mark Seemann的一些关于AutoMocking的博客文章,并尝试从那里开始工作,但我没有走得太远.
这是我的测试看起来没有AutoFixture:
[Fact]
public void GetXml_ReturnsCorrectXElement()
{
// Arrange
string xmlString = @"
<mappings>
<mapping source='gcnm_loan_amount_min' target='gcnm_loan_amount_min_usd' />
<mapping source='gcnm_loan_amount_max' target='gcnm_loan_amount_max_usd' />
</mappings>";
string settingKey = "gcCreditApplicationUsdFieldMappings";
Mock<ISettings> settingsMock = new Mock<ISettings>();
settingsMock.Setup(s => s.Get(settingKey)).Returns(xmlString);
ISettings settings = settingsMock.Object;
ITracingService tracing = new Mock<ITracingService>().Object;
XElement expectedXml = XElement.Parse(xmlString);
IMappingXml sut = new SettingMappingXml(settings, tracing);
// Act
XElement actualXml = sut.GetXml();
// Assert
Assert.True(XNode.DeepEquals(expectedXml, actualXml));
}
Run Code Online (Sandbox Code Playgroud)
这里的故事很简单 - 确保使用正确的密钥(这是硬编码/属性注入)SettingMappingXml
查询ISettings
依赖关系并将结果返回为XElement
.在 …
Autofac允许使用.AsImplementedInterfaces()或链接非常容易地将多个接口解析到同一个实例.As <>()与.SingleInstance()一起调用.这也可以用TinyIoC完成吗?我只发现了如何注册同一个接口的多个实现,但没有办法链接注册等.
根据我的理解,这是IoC容器的一个非常重要的功能,不是吗?
我只是在南希弄湿了脚.我很高兴看到Wiki中的测试过程,但是当我尝试以下操作时,我无法通过测试来完成测试.
使用VS2010
Empty ASP.NET Web Application Project
:Notify.AppInstall-Package Nancy.Hosting.AspNet
Class Library
项目:Notify.UnitTestsInstall-Package Nancy.Testing
Install-Package XUnit
使用DefaultNancyBootstrapper
测试失败了HttpStatusCode.NotFound
.
如果我用以下内容替换bootstrapper
定义:
var bootstrapper = new ConfigurableBootstrapper(
with =>
with.Module<NotifyModule>());
Run Code Online (Sandbox Code Playgroud)
然后测试通过. 我不明白为什么使用DefaultNancyBootstrapper的SDHP不起作用?我做错了什么让它破裂,还是我在理解中遗漏了细节?
NotifyModule
using Nancy;
public class NotifyModule : NancyModule {
public NotifyModule() {
Get["/"] = _ => HttpStatusCode.OK;
}
}
Run Code Online (Sandbox Code Playgroud)
BaseUrlSpec
using Nancy;
using Nancy.Testing;
using Notify.App;
using Xunit;
public class BaseUrlSpec
{
[Fact]
public void ShouldRespondOk()
{
var bootstrapper = new …
Run Code Online (Sandbox Code Playgroud) 鉴于此系统要测试:
public class MySut
{
private readonly IHardToMockDependency _hardToMockDependency;
public MySut(IHardToMockDependency hardToMockDependency,
IOtherDependency otherDependency)
{
_hardToMockDependency = hardToMockDependency;
}
public string GetResult()
{
return _hardToMockDependency.GetResult();
}
}
public interface IOtherDependency { }
public interface IHardToMockDependency
{
string GetResult();
}
Run Code Online (Sandbox Code Playgroud)
而这个单元测试:
internal class FakeHardToMockDependency : IHardToMockDependency
{
private readonly string _result;
public FakeHardToMockDependency(string result)
{
_result = result;
}
public string GetResult()
{
return _result;
}
}
public class MyTests
{
[Fact]
public void GetResultReturnsExpected()
{
string expectedResult = "what I …
Run Code Online (Sandbox Code Playgroud) 有什么区别
Method(int arg0 = 0) vs Method([Optional] int arg0 = 0);
Run Code Online (Sandbox Code Playgroud)
每当我试图调用这个方法时,编译器就会说出它的模糊情况.我知道为什么它是模棱两可的情况,我的兴趣是那两个如果他们促进相同的事情=可选参数之间的差异.然而,他们以不同的方式这样做,在视觉上清单 - 不知道引擎盖下.
有人向我指出,第一种方式实际上是DEFAULT使用,这意味着你将初始化默认值,而第二种方式是可选的,它用于你不定义任何默认值的情况 - 虽然它有意义,但它们都可以很容易分配值而不是.它们的真正区别和用途是什么?
我有一个非常基本的问题,使用Simple Injector将依赖项注入MVC控制器.我以前使用过SimpleMap的Simple Injector.
MVC的版本是4.5,它是从NuGet安装的Simple Injector的最新版本.
查看HomeController的/ Index视图时出现的错误是:
HomeController类型的构造函数包含IContext类型的参数,其名称为"context",未注册.请确保IContext已在容器中注册,或更改HomeController的构造函数.
控制器如下:
public class HomeController : Controller
{
public HomeController(IContext context) { }
public ActionResult Index() { }
}
Run Code Online (Sandbox Code Playgroud)
IContext只是一个简单的界面:
public interface IContext
{
}
Run Code Online (Sandbox Code Playgroud)
具体的IContext实现也很简单,只是常规DbContext的包装器.
public class DbContext : System.Data.Entity.DbContext, IContext
{
}
Run Code Online (Sandbox Code Playgroud)
有关信息,IContext接口与DbContext实现位于不同的VS Project/Assembly中.这些由MVC项目引用.
我在Global.asax.cs中有以下内容:
protected void Application_Start()
{
var container = new Container();
container.Register<IContext, DbContext>();
container.RegisterMvcControllers(System.Reflection.Assembly.GetExecutingAssembly());
container.RegisterMvcAttributeFilterProvider();
container.Verify();
DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(container));
// Regular MVC startup
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
Run Code Online (Sandbox Code Playgroud)
这是堆栈跟踪:
[ActivationException: The constructor of the type HomeController contains the …
Run Code Online (Sandbox Code Playgroud) c# asp.net-mvc dependency-injection simple-injector asp.net-mvc-4
我ofType<'T>
基于以下方式为序列创建了一个可管道的函数Enumerable.OfType<'T>()
:
let ofType<'T> (sequence : _ seq) = sequence.OfType<'T>()
Run Code Online (Sandbox Code Playgroud)
在同一个.fsx
文件中使用它可以正常工作; 我把它放入模块时仍然会这样做:
module Seq =
let ofType<'T> (sequence : _ seq) = sequence.OfType<'T>()
Run Code Online (Sandbox Code Playgroud)
当我将其移动到另一个脚本文件中时(它能够从其他地方访问它)将它包装在另一个顶级模块中时它停止工作:
module Prelude =
open System.Linq
module Seq =
let ofType<'T> (sequence : _ seq) = sequence.OfType<'T>()
Run Code Online (Sandbox Code Playgroud)
我从原始脚本文件中引用它,打开Prelude
模块并调用函数如下:
let getXmlIncludes (xtype : Type) =
xtype.GetCustomAttributes() |> Seq.ofType<XmlIncludeAttribute>
Run Code Online (Sandbox Code Playgroud)
这导致Seq.ofType<XmlIncludeAttribute>
被标记为错误,带有消息
error FS0001: Type mismatch. Expecting a
Collections.Generic.IEnumerable<Attribute> -> 'a
> but given a
Collections.Generic.IEnumerable<Attribute> -> Collections.Generic.IEnumerable<XmlIncludeAttribute>
The type 'obj' does …
Run Code Online (Sandbox Code Playgroud) c# ×5
autofixture ×2
automocking ×2
f# ×2
unit-testing ×2
xunit.net ×2
.net ×1
asp.net-mvc ×1
composition ×1
generics ×1
moq ×1
nancy ×1
tinyioc ×1
winforms ×1
xunit ×1