代码之家  ›  专栏  ›  技术社区  ›  catfood

具有“has property X”约束的泛型函数?

  •  16
  • catfood  · 技术社区  · 17 年前

    foreach (object x in BigComInterface.Chickens)
    {
        (x as Chicken).attribute = value;
    }
    foreach (object x in BigComInterface.Ducks)
    {
        (x as Duck).attribute = value;
    }
    

    但是分配属性很可能(由于不可避免的特定于应用程序的原因)引发我想要从中恢复的异常,因此我真的希望对每个异常进行尝试/捕获。因此:

    foreach (object x in BigComInterface.Chickens)
    {
        try
        {
            (x as Chicken).attribute = value;
        }
        catch (Exception ex)
        {
            // handle...
        }
    }
    foreach (object x in BigComInterface.Ducks)
    {
        try
        {
            (x as Duck).attribute = value;
        }
        catch (Exception ex)
        {
            // handle...
        }
    }
    

    显然,这样做会更干净:

    foreach (object x in BigComInterface.Chickens)
    {
        SetAttribute<Chicken>(x as Chicken, value);
    }
    foreach (object x in BigComInterface.Ducks)
    {
        SetAttribute<Duck>(x as Duck, value);
    }
    
    void SetAttribute<T>(T x, System.Object value)
    {
        try
        {
            x.attribute = value;
        }
        catch
        {
            // handle...
        }
    }
    

    x 值可以是任何类型,因此编译器无法解析 属性 . 如果他们这样做了,我就可以在 T

    在我的幻想中,我想要的是一种约束,要求论点 具有.attribute属性 不管它是否实现了给定的接口。也就是说,

    void SetAttribute<T>(T x, System.Object value) where T:hasproperty(attribute)
    

    除了为鸡、鸭、牛、羊等的每只鸡剪切/粘贴这个小的try/catch块之外,我不知道从这里该做什么。

    我的问题是:如果在编译时无法知道实现某个对象上的特定属性的接口,那么想要调用该属性的问题有什么好的解决方法?

    4 回复  |  直到 17 年前
        1
  •  8
  •   danielschemmel    17 年前

    class Chicken
    {
        public string attribute { get; set; }
    }
    
    class Duck
    {
        public string attribute { get; set; }
    }
    
    interface IHasAttribute
    {
        string attribute { get; set; }
    }
    
    class ChickenWrapper : IHasAttribute
    {
        private Chicken chick = null;
        public string attribute
        {
            get { return chick.attribute; }
            set { chick.attribute = value; }
        }
        public ChickenWrapper(object chick)
        {
            this.chick = chick as Chicken;
        }
    }
    
    class DuckWrapper : IHasAttribute
    {
        private Duck duck = null;
        public string attribute
        {
            get { return duck.attribute; }
            set { duck.attribute = value; }
        }
        public DuckWrapper(object duck)
        {
            this.duck = duck as Duck;
        }
    }
    
    void SetAttribute<T>(T x, string value) where T : IHasAttribute
    {
        try
        {
            x.attribute = value;
        }
        catch
        {
            // handle...
        }
    }
    
        2
  •  5
  •   Reed Copsey    17 年前

    不幸的是,目前这是一个棘手的问题。在C#4中,动态类型在这方面可能会有很大帮助。COM互操作是dynamic真正闪耀的地方之一。

    但是,同时,允许您拥有任何类型的对象(对接口没有限制)的唯一选项是恢复使用反射。

    您可以使用反射来查找“attribute”属性,并在运行时设置其值。

        3
  •  3
  •   Mike de Klerk    9 年前

    为了不违反干燥原则,可以使用反射。如果您真的想使用泛型,那么可以为每种类型的第三方对象使用一个包装器类,并让包装器实现一个可用于约束泛型类型参数的接口。

        void SetAttribute(object chickenDuckOrWhatever, string attributeName, string value)
        {
            var typeOfObject = chickenDuckOrWhatever.GetType();
            var property = typeOfObject.GetProperty(attributeName);
            if (property != null)
            {
                property.SetValue(chickenDuckOrWhatever, value);
                return;
            }
    
            //No property with this name was found, fall back to field
            var field = typeOfObject.GetField(attributeName);
            if (field == null) throw new Exception("No property or field was found on type '" + typeOfObject.FullName + "' by the name of '" + attributeName + "'.");
            field.SetValue(chickenDuckOrWhatever, value);
        }
    

    如果要提高代码的性能,可以缓存每种类型的FieldInfo和PropertyInfo chickenDuckOrWhatever 思考之前先查字典。

    提示:不必硬编码 attributeName nameof . 比如nameof(Chicken.AttributeName)。

        4
  •  2
  •   Andrew Hare    17 年前

    不幸的是,这样做的唯一方法是使用定义该属性并在所有类型上实现的接口来约束类型参数。

    dynamic