简体   繁体   English

不使用扩展方法而使用通用逻辑扩展C#接口

[英]Extending a C# interface with common logic without using Extension Methods

Is there a way to extend an interface without using Extension Methods? 有没有不用扩展方法即可扩展接口的方法?

If I define some interface with a couple of get/set strings for example : 如果我用一些get / set字符串定义一些接口,例如:

public interface IMyItem
{
    string Title { get; set; }
    string Description { get; set; }
}

I'd like add some simple validation to these interfaces, but without having to re-define the logic or force some form of inheritance. 我想对这些接口添加一些简单的验证,但不必重新定义逻辑或强制某种形式的继承。

Currently I'm using Extension Methods, like this : 目前,我正在使用扩展方法,如下所示:

public static class MyItemExtensions
{
    public static bool ERROR(this IMyItem item)
    {
        return item.TITLE_ERROR() || item.DESCRIPTION_ERROR();
    }

    public static bool TITLE_ERROR(this IMyItem item)
    {
        return string.IsNullOrEmpty(item.Title);
    }

    public static bool DESCRIPTION_ERROR(this IMyItem item)
    {
        return string.IsNullOrEmpty(item.Description);
    }
}

Doing this works and I can have: 这样做有效,我可以拥有:

public class Item : IMyItem
{
   public string Title { get; set; }
   public string Description { get; set; }
}

public static class app
{
    public static void go()
    {
        var item = new Item
        {
            Title = "My Item Title", 
            Description = ""
        };

        Console.log(item.ERROR());
    }
}

But I'd prefer ERROR , TITLE_ERROR & DESCRIPTION_ERROR to be get/sets - Is there a way to achieve the same but expose Get/Set properties rather than Extension Methods? 但是我更喜欢将ERRORTITLE_ERRORDESCRIPTION_ERROR TITLE_ERROR获取/设置-是否可以实现相同的功能,但要公开获取/设置属性而不是扩展方法?

Update 11/06/2014 更新11/06/2014

As many have suggested, an abstract class would be an obvious solution based on the example, but the types need to implement multiple interfaces. 正如许多人所建议的,基于该示例, abstract类将是显而易见的解决方案,但是类型需要实现多个接口。

While it may be possible to arrange the inheritance it's an unnecessary complication and restriction on the types. 尽管可以安排继承,但这是不必要的复杂性和类型限制。

The added benefit of using Extension Methods for validation on these interfaces allows for context specific & shared logic to be used, via namespacing. 在这些接口上使用扩展方法进行验证的附加好处是可以通过命名空间使用特定于上下文的共享逻辑。

Multiple ERROR(this IMyItem item) Extension Methods could be defined for the interface, on different namespaces. 可以为接口在不同的名称空间上定义多个ERROR(this IMyItem item)扩展方法。 One that checks Both the TITLE_ERROR and DESCRIPTION_ERROR and another might only test one of the properties. 一个同时检查TITLE_ERRORDESCRIPTION_ERROR ,另一个可能只测试其中一个属性。 Then, depending on the context, the relevant namespace can be referenced and the shared validation for that item be executed. 然后,根据上下文,可以引用相关的名称空间,并执行该项目的共享验证。

I'll look at Microsoft's validator, but it looks rather verbose and I really wanted these states as properties for type, as it makes the code that uses them much easier to use. 我将看一下Microsoft的验证器,但是它看起来很冗长,我确实希望这些状态作为类型的属性,因为它使使用它们的代码更易于使用。

Additionally, these are very simple examples, some of the validation much more complex and some cases require interaction with other web services - albeit against a cache of web service data within the AppDomain. 此外,这些都是非常简单的示例,有些验证要复杂得多,有些情况需要与其他Web服务进行交互-尽管是针对AppDomain中的Web服务数据缓存。

Currently these interface Extension Methods feel like the best solution. 当前,这些interface扩展方法感觉是最好的解决方案。

I think a proper solution would be to use an abstract class instead of an interface . 我认为适当的解决方案是使用abstract class而不是interface

What you share here is a common validation logic which will be valid for any class implementing IMyItem . 您在这里分享的是一个通用的验证逻辑,该逻辑对实现IMyItem任何类均有效。 As so, i'd recommand you create an abstract class as a base for all items, that way they can all reuse that validation code. 因此,我建议您创建一个abstract class作为所有项目的基础,这样它们都可以重用该验证代码。 You can even make those properties virtual, so one could extend that validation code: 您甚至可以将这些属性虚拟化,因此可以扩展该验证代码:

public abstract class ItemBase : IMyItem
{
    public string cTitle { get; set; }
    public string cDescription { get; set; }

    public virtual bool Error 
    { 
      get { return TitleError || DescriptionError; } 
    }

    public virtual bool TitleError 
    { 
      get { return string.IsNullOrEmpty(cTitle); } 
    }

    public virtual bool DescriptionError 
    { 
      get { return string.IsNullOrEmpty(cDescription); } 
    }
}

Seems like you're reinventing the wheel. 似乎您正在重新发明轮子。 Microsoft already created a fairly good validation framework that does not require Entity Framework or MVC. Microsoft已经创建了一个不需要实体框架或MVC的相当不错的验证框架 Simply add a reference to System.ComponentModel.DataAnnotations and the using System.ComponentModel.DataAnnotations to a class: 只需添加一个参考System.ComponentModel.DataAnnotationsusing System.ComponentModel.DataAnnotations一类:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;

public class Program
{
    public static void Main()
    {
        // required if you use the MetdataType attribute
        TypeDescriptor.AddProviderTransparent(
            new AssociatedMetadataTypeTypeDescriptionProvider(typeof(MyItem),
                typeof(IMyItemValidation)),
            typeof(MyItem));


        var item = new MyItem();
        var context = new ValidationContext(item, 
          serviceProvider: null, 
          items: null);
        var results = new List<ValidationResult>();

        var isValid = Validator.TryValidateObject(item, context, results);

        if (!isValid)
        {
            foreach (var validationResult in results)
            {
                Console.WriteLine(validationResult.ErrorMessage);
            }
        }

        Console.ReadKey();
    }

    [MetadataType(typeof(IMyItemValidation))]
    public class MyItem : IMyItem
    {
        public string cTitle { get; set; }
        public string cDescription { get; set; }
    }

    public interface IMyItem
    {
        string cTitle { get; set; }
        string cDescription { get; set; }
    }

    public interface IMyItemValidation
    {
        [Required]
        string cTitle { get; set; }
        [Required]
        string cDescription { get; set; }
    }

    /* 

    // alternatively you could do either of these as well:
    // Derive MyItem : MyItemBase

    // contains the logic on the base class
    public abstract MyItemBase
        [Required]
        public string cTitle { get; set; }
        [Required]
        public string cDescription { get; set; }
    }

    // or

    // Derive MyItem : MyItemBase
    // contains the logic on the base class using MetadataType
    [MetadataType(typeof(IMyItemValidation))]
    public abstract MyItemBase
        public string cTitle { get; set; }
        public string cDescription { get; set; }
    }


}

Output 产量

The cTitle field is required. cTitle字段是必填字段。

the cDescription field is required. cDescription字段为必填。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM