使用匿名函数初始化委托

时间:2018-05-20 12:32:27

标签: c# anonymous-function

我有一些作为各种模型存在的硬件,我正在编写将使用各种GUI控制它的软件。我有一个代表硬件的类,它具有电压,速率,大小等属性。我想通过创建如下所示的类来简化对这些属性的访问:

    public class CProperty
    {
        public CProperty(string name, Func<object> get, Action<object> set)
        {
            this.name = name;
            this.get = get;
            set = set;
        }
        private string name;
        public Func<object> get;
        public Action<object> set;
        private object value;
    }

然后我可以按如下方式初始化这些数组:

    public CProperty[] Properties = new CProperty[]
    {
        new CProperty(
            "name1",
            new Func<float>(() => value),
            new Action<float>((v) => value = v)
            ),
    };

然后可以将数组转换为具有索引器的集合类,该索引器使用属性名称作为索引。 但是当我尝试这样做时,在数组初始化字段&#34; value&#34;不在set和get函数的初始值设定范围内,我不明白为什么。

2 个答案:

答案 0 :(得分:0)

您无法将Func<float>转换为Func<object>,也无法将Action<float>转换为Action<object>。这是因为协方差和逆变不能很好地与价值类型相匹配(最终协方差和逆变是手法,而不是类型之间的真实转换,因此要求两种类型具有相同的内存布局...... reference是一个引用,因此很容易在两个引用之间进行协方差/逆变,但是float是一个值类型,它与作为引用类型的object非常不同)

在这两种情况下,您都应该向CProperty添加Func/Action类型的参数,例如Func<CProperty, float>(cp => cp.value),但value必须是public ...这是因为匿名方法是&#34;静态&#34;,他们无法免费访问this引用,因此您必须将其作为参数传递...

public class CProperty
{
    public CProperty(string name, Func<CProperty, object> get, Action<CProperty, object> set)
    {
        this.name = name;
        this.get = get;
        this.set = set;
    }
    private string name;
    public Func<CProperty, object> get;
    public Action<CProperty, object> set;
    public object value;
}

var properties = new CProperty[]
{
    new CProperty(
        "name1",
        new Func<CProperty, object>(cp => cp.value),
        new Action<CProperty, object>((cp, v) => cp.value = v)
    ),
};

或者您甚至可以捕获this

public class CProperty
{
    public CProperty(string name, Func<CProperty, Func<object>> get, Func<CProperty, Action<object>> set)
    {
        this.name = name;
        this.get = get(this);
        this.set = set(this);
    }
    private string name;
    public Func<object> get;
    public Action<object> set;
    public object value;
}   

var properties = new CProperty[]
{
    new CProperty(
        "name1",
        cp => new Func<object>(() => cp.value),
        cp => new Action<object>((v) => cp.value = v)
    ),
};

最后我认为你离你想要的还很远......你必须回到CProperty班的设计委员会。

答案 1 :(得分:0)

我不太了解你将在这里实现的目标。

查看代码,我可以说你正在尝试重新实现一个property概念 - 你有支持字段value和getter用代表表达的setter。

看一下描述,

  

带有索引器的集合类,该索引器使用属性名称作为索引

我可以说你想在运行时通过名字访问对象的属性 - 这可以使用reflection

至少,我能够回答这部分

  

但是当我尝试这样做时,在数组初始化中,字段“value”不在set和get函数的初始化器中,并且我不明白为什么。

正如编译器所说,这件事与范围界定有关。在这种情况下区分Lexical and dynamic scope是有意义的。 词法或静态范围是通过程序源代码中的位置定义的,而动态范围是基于实际执行路径构建的。 C#具有词法作用域,因此,从CProperty类外部声明的委托不能访问value字段,尽管它们实际上是在内部调用的。

value是一个字段,它位于CProperty类的范围内,因此它仅对此类成员可见。外部世界可以通过CProperty实例访问此字段(但仅当您创建字段public时)。

var prop = new CProperty(/*ctor parameters*/)
var value = prop.value;