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

时间:2014-06-10 08:56:33

标签: c# .net c#-4.0 interface extension-methods

有没有办法在不使用扩展方法的情况下扩展接口?

如果我用一些get / set字符串定义一些接口,例如:

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

我想为这些接口添加一些简单的验证,但没有 重新定义逻辑或强制某种形式的继承。

目前我正在使用扩展方法,如下所示:

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);
    }
}

这样做有用,我可以:

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());
    }
}

但我更喜欢ERRORTITLE_ERROR& DESCRIPTION_ERROR 得到/集 - 有没有办法实现相同但暴露 获取/设置属性而不是扩展方法?

2014年11月6日更新

正如许多人所建议的,基于该示例,abstract类将是一个明显的解决方案,但类型需要实现多个接口。

虽然可以安排继承,但这是对类型的不必要的复杂性和限制。

使用扩展方法对这些接口进行验证的额外好处允许特定于上下文的&通过命名空间共享逻辑。

可以在不同的名称空间上为接口定义多个ERROR(this IMyItem item)扩展方法。检查TITLE_ERRORDESCRIPTION_ERROR以及另一个可能只测试其中一个属性的人。然后,根据上下文,可以引用相关的命名空间,并执行该项的共享验证。

我会看看微软的验证器,但它看起来相当冗长,我真的希望这些状态作为类型的属性,因为它使得使用它们的代码更容易使用。

此外,这些是非常简单的示例,一些验证要复杂得多,有些情况需要与其他Web服务交互 - 尽管AppDomain中的Web服务数据缓存。

  

目前,这些interface扩展方法感觉是最佳解决方案。

2 个答案:

答案 0 :(得分:5)

我认为合适的解决方案是使用abstract class代替interface

您在这里分享的是一个通用的验证逻辑,它对任何实现IMyItem的类都有效。因此,我建议您创建一个abstract class作为所有项目的基础,这样他们就可以重用该验证代码。您甚至可以将这些属性设置为虚拟,因此可以扩展该验证代码:

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); } 
    }
}

答案 1 :(得分:0)

好像你正在重新发明轮子。 Microsoft已经创建了一个不需要Entity Framework或MVC的公平good validation framework。只需将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; }
    }


}

输出

  

cTitle字段是必需的。

     

cDescription字段是必需的。