2

我有一些适用于这样的颜色结构的代码

public void ChangeColor()
{
    thisColor.R = thisColor.R + 5;
}

现在我需要创建一个方法来根据传递的内容更改不同的变量。这是代码现在的样子。

public void ChangeColor(int RGBValue)
{
    switch(RGBValue)
    {
        case 1:
            thisColor.R = thisColor.R + 5;
            break;
        case 2:
            thiscolor.B = thisColor.B + 5;
            break;
    }
}

现在,这是我通常不会质疑的问题,我只是在它周围抛出一个#region 语句并称之为一天,但这只是我所拥有的一个例子,实际函数很长。

我希望它看起来像这样:

public void ChangeColor(int RGBValue)
{
    thiscolor.RGBValue = thiscolor.RGBValue;
}

所以本质上该值将引用正在使用的变量。有这个名字吗?这就是反射的用途吗?或类似的东西......有没有办法做到这一点?

4

7 回答 7

5

我不是 100% 确定这是否是你想要的。但是对于给定的示例,听起来这可能就是您所追求的。

您也许可以使用以下ref关键字:

public void ChangeColor(ref int color)
{
    color += 5;
}

void SomeMethod()
{
    ChangeColor(ref thisColor.R); //Change the red value
    ChangeColor(ref thisColor.B); //Change the blue value
}
于 2010-03-27T00:51:53.690 回答
2

这绝对不是反射的目的。事实上,这里似乎存在许多问题。让我们在这里回顾一下 - 您想要更改以下方法:

public void ChangeColor(int RGBValue)
{
    switch(...)
    {
        case ...
        case ...
        case ...
    }
}

变成这样:

public void ChangeColor(int RGBValue)
{
    thisColor.{something-from-RGBValue} += 5;
}

这样做的问题是:

  • 方法的名称ChangeColor, 并没有准确地描述该方法的实际作用。也许这是匿名化的产物,但对于该方法来说,它是一个可怕的名称。

  • 参数 ,RGBValue不能准确地描述参数是什么或做什么。名称RGBValue和类型int使它听起来像一个实际的 RGB 颜色值,即 0x33ccff 表示浅蓝色。相反,它选择将设置 R、G 或 B 中的哪一个。

  • 该参数只有 3 个有效值,但可能值的范围完全不受限制。这是错误的秘诀。更糟糕的是,单个值在方法中被用作幻数。

  • 但也许最重要的是,您所要求的“干净/快速方法”正是该方法声称提供的抽象! 您正在编写一种增强色调的方法,为了保持方法简短,您要求......一种增强色调的方法。这没有意义!

我只能假设您想要这样做,因为您可能想要对颜色执行许多不同的操作,例如:

public void Brighten(...) { ... }
public void Darken(...) { ... }
public void Desaturate(...) { ... }
public void Maximize(...) { ... }

等等等等。而且你试图避免switch为所有人写声明。

很好,但不要完全消除switch;这是迄今为止编写此代码的最有效最易读的方式!更重要的是把它提炼成一个 switch而不是多个,并解决上面提到的其他问题。首先,让我们从一个合理的参数类型开始,而不是int- 创建一个枚举:

public enum PrimaryColor { Red, Green, Blue };

现在,从我们可能想要对复合颜色的一种原色执行许多操作的想法开始,因此编写通用方法:

protected void AdjustPrimaryColor(PrimaryColor pc, Func<byte, byte> adjustFunc)
{
    switch (pc)
    {
        case PrimaryColor.Red:
            internalColor.R = adjustFunc(internalColor.R);
        case PrimaryColor.Green:
            internalColor.G = adjustFunc(internalColor.G);
        default:
            Debug.Assert(pc == PrimaryColor.Blue,
                "Unexpected PrimaryColor value in AdjustPrimaryColor.");
            internalColor.B = adjustFunc(internalColor.B);
    }
}

这种方法简短易读,并且可能永远不必更改。这是一个很好,干净的方法。现在我们可以很容易地编写各个动作方法:

public void Brighten(PrimaryColor pc)
{
    AdjustPrimaryColor(pc, v => v + 5);
}

public void Darken(PrimaryColor pc)
{
    AdjustPrimaryColor(pc, v => v + 5);
}

public void Desaturate(PrimaryColor pc)
{
    AdjustPrimaryColor(pc, v => 0);
}

public void Maximize(PrimaryColor pc)
{
    AdjustPrimaryColor(pc, v => 255);
}

这样做的(显着)优点是:

  • 枚举类型可防止调用者搞砸并传入无效的参数值。

  • 通用Adjust方法易于阅读,因此易于调试和维护。它也将比任何基于反射或基于字典的方法表现更好 - 并不是说​​性能在这里可能是一个问题,但我主要是这样说,它肯定不会更糟

  • 您不必编写重复的switch语句。每个单独的修饰符方法恰好是一行。

最终,在某个地方,您实际上将不得不编写一些代码,我更希望代码是一个极其简单的switch语句,而不是一团糟的反射、委托、字典等。关键是尽可能概括这项工作可能的; 一旦你完成了这些并创建了那个抽象,那么你就可以开始编写单行方法来完成“真正的”工作。

于 2010-03-27T03:21:04.677 回答
1

这有点尴尬,但你可以像这样传递一个属性'by ref':

    int ThisColor { get; set; }

    public void ChangeColor(Func<int> getter, Action<int> setter)
    {
        setter(getter() + 5);
    }

    public void SomeMethod()
    {
        ChangeColor(() => ThisColor, (color) => ThisColor = color);
    }

这比反射便宜,并且在编译时检查(使用反射,您必须将字符串传递给 GetProperty 调用,并且字符串名称可能会在以后的重构中与属性名称不同。)

于 2010-03-27T02:57:35.953 回答
0

我倾向于使用字典,而不是我怀疑可能最终成为大型 switch 语句的字典,所以如果你创建了一个

Dictionary<int,Func<int,int>> map = new Dictionary<int, Func<int, int>>();

字典中的每个项目都可以输入并返回新值

所以你可以调用你的方法

        public int ChangeColor(int rgbValue)
    {
        return map[rgbValue](rgbValue);
    }

它将执行特定于您插入的 Rgb 值的委托,要分配一个委托,您只需向地图添加一个新条目

map.Add(5,x => x+5);
于 2010-03-27T01:04:14.513 回答
0

这可能是您正在寻找的,但您可能想要添加一些错误处理。
它适用于任何类型的公共财产;并设置;方法。
如果你想有办法减少“魔术字符串”的使用。

public static void ChangeProperty<T>(this object obj, string propertyName, Func<T,T> func)
{
    var pi = obj.GetType().GetProperty(propertyName);
    pi.SetValue(obj, func((T)pi.GetValue(obj, null)), null);
}
public void Change()
{
    thisColor.ChangeProperty<int>("R", (x) => x + 5);
}
于 2010-03-27T02:27:14.170 回答
0

如果我理解正确,您想编写一个方法,该方法采用一些符号(或属性名称)并使用此符号定义的结构修改属性。这在 C# 中不容易实现(您当然可以使用反射,但是......)。

Dictionary您可以使用包含委托来读取和写入属性的值来做类似的事情。但是,这仍然会有点冗长,因为您需要初始化字典。无论如何,代码可能如下所示:

var props = new Dictionary<string, Tuple<Func<Color, int>, Action<Color, int>>> 
  { "R", Tuple.Create(c => c.R, (c, r) => c.R = r),
    "G", Tuple.Create(c => c.G, (c, g) => c.G = g),
    "B", Tuple.Create(c => c.B, (c, b) => c.B = b) };

这将创建一个字典,其中包含作为键的字符串(属性名称)和一个包含每个属性的 getter 委托和 setter 委托的元组。现在您的ChangeColor方法可能如下所示:

public void ChangeColor(string propName) {
  var getSet = props[propName];    
  getSet.Item2(thisColor, getSet.Item1(thisColor) + 5);
}

Get如果您将自己的类型与property 和Setproperty 一起使用,而不是Tuple使用名为Item1and的属性,则代码将更具可读性Item2。这种解决方案在某些场景下可能有用,但在初始化字典时仍需要显式列出所有属性。

于 2010-03-27T02:07:18.120 回答
0

好吧,由于您给出了一个非常简化的示例,因此很难说出真正发生了什么。

但是,我真正阅读的是,您希望拥有一种方法,该方法将根据方法的参数之一对本地状态执行多种可能的修改之一。

现在,操作是否相同,除了它正在做什么?

最终,您必须有一些可以理解的代码,将输入映射到所需的操作。可以概括多少取决于动作的相似程度(如果它总是“向属性添加 5”,那么您有更多的概括选项......)。

你有一些选择是:

  1. 编写一个封装 Color 结构的类。
  2. Action按照 Kev Hunter 的建议,使用 s 的查找表。
  3. 写一个 switch 语句。
  4. 传入一个包含可以在内部数据上执行的虚拟方法的参数(或直接传入一个 Action<> ) - 避免查找

而且……就是这样,真的。其中哪一个最有意义可能更多地取决于您的实际用例(我们实际上并没有很多信息),而不是其他任何事情。

于 2010-03-27T02:48:19.147 回答