减少重复代码

发布于 2024-08-26 13:23:34 字数 696 浏览 7 评论 0原文

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

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

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

I have some code that works on the color structure like this

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

Now I need to make a method that changes a different variable depending on what it is passed. Here is what the code looks like now.

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

Now, this is something I would normally never question, I'd just throw a #region statement around it and call it a day, but this is just an example of what I have, the actual function is quite long.

I want it to look like this:

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

So essentially the value would refer to the variable being used. Is there a name for this? Is this what Reflection is for? Or something like that... Is there a way to do this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

等往事风中吹 2024-09-02 13:23:34

我不确定这是否是您想要的。但通过给定的示例,听起来这可能就是您所追求的。

您也许可以使用 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
}

I'm not 100% sure if this is what you want. But with the given example, it sounds like this might be what you're after.

you might be able to use the ref keyword:

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
}
别忘他 2024-09-02 13:23:34

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

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

现在,从这样的想法开始:我们可能想要对复合的其中一种原色执行许多操作color,所以编写通用方法:

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方法易于阅读,因此易于调试和维护。它也将比任何基于反射或基于字典的方法表现得更好 - 性能并不是这里关注的问题,但我主要是想指出它肯定不会更糟 em>.

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

最终某个地方,你实际上必须编写一些代码,我更希望代码是一个极其简单的 switch 语句而不是一堆混乱的反思、代表、字典等。关键是尽可能地概括这项工作;一旦您完成了该操作并创建了该抽象,然后您就可以开始编写一行方法来完成“真正的”工作。

This is definitely not what reflection is for. In fact, there seem to be a number of issues here. Let's review here - you want to change the following method:

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

Into something like this:

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

The problems with this are:

  • The name of the method, ChangeColor, does not precisely describe what the method actually does. Perhaps this is an artifact of anonymization, but nevertheless it's a terrible name for the method.

  • The parameter, RGBValue, does not accurately describe what the argument is or does. The name RGBValue and the type int makes it sound like an actual RGB color value, i.e. 0x33ccff for a light blue. Instead it chooses which of R, G, or B will be set.

  • There are only 3 valid values for the parameter, and yet the range of possible values is completely unrestricted. This is a recipe for bugs. Worse, individual values are used as magic numbers inside the method.

  • But perhaps most important of all, the "clean/quick method" you are asking for is precisely the abstraction that this method purports to provide! You're writing a method that intensifies the hue, and in order to keep the method short, you're asking for... a method to intensify the hue. It doesn't make sense!

I can only assume that you want to do this because you have many different things you might want to do to a Color, for example:

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

And so on and so forth. And you're trying to avoid writing switch statements for all.

Fine, but don't eliminate the switch entirely; it is by far the most efficient and readable way to write this code! What's more important is to distill it down to one switch instead of many, and fix the other problems mentioned above. First, let's start with a reasonable parameter type instead of an int - create an enumeration:

public enum PrimaryColor { Red, Green, Blue };

Now, start from the idea that there may be many actions we want to perform on one of the primary colors of a composite color, so write the generic method:

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

This method is short, easy to read, and will likely never have to change. It is a good, clean method. Now we can write the individual action methods quite easily:

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

The (significant) advantages to this are:

  • The enumeration type prevents callers from screwing up and passing in an invalid parameter value.

  • The general Adjust method is easy to read and therefore easy to debug and easy to maintain. It's also going to perform better than any reflection-based or dictionary-based approach - not that performance is likely a concern here, but I'm mainly saying this to note that it certainly isn't going to be worse.

  • You don't have to write repeated switch statements. Each individual modifier method is exactly one line.

Eventually, somewhere, you're actually going to have to write some code, and I would much rather that code be an extremely simple switch statement than a mess of reflection, delegates, dictionaries, etc. The key is to generalize this work as much as possible; once you've done that and created that abstraction, then you can start writing one-liner methods to do the "real" work.

离旧人 2024-09-02 13:23:34

这有点尴尬,但是您可以像这样“通过引用”传递属性:

    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 调用和字符串名称在以后的重构中可能会与属性名称有所不同。)

It's a bit awkward, but you can pass a property 'by ref' like this:

    int ThisColor { get; set; }

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

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

This is less expensive than reflection and it's compile-time checked (with reflection, you'd have to pass a string to a GetProperty call and the string name could potentially diverge from the property name in later refactoring.)

智商已欠费 2024-09-02 13:23:34

我倾向于使用字典,而不是我怀疑最终可能成为一个大的 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);

I would tend to use a dictionary rather than what i suspect could end up being a large switch statement so if you created a

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

Each item in your dictionary could take then input and return the new value

so you your method you would be able to call

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

which will execute the delegate specific for the Rgb value you insert, to assign a delegate you simply add a new entry to the map

map.Add(5,x => x+5);
千秋岁 2024-09-02 13:23:34

如果我理解正确,您希望编写一个方法,该方法采用一些符号(或属性名称)并使用此符号定义的结构修改属性。这在 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 属性和 Set 属性而不是Tuple 具有名为 Item1Item2 的属性。该解决方案在某些场景中可能有用,但您仍然需要在初始化字典时显式列出所有属性。

If I understand you correctly, you'd like to write a method that takes some symbol (or property name) and modifies the property of the structure using defined by this symbol. This isn't easily possible in C# (you could of course use reflection, but...).

You could do similar thing using Dictionary containing delegates for reading and writing the value of the property. However, that will still be a bit lengthy, because you'll need to initialize the dictionary. Anyway, the code might look like this:

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

This creates a dictionary that contains string (name of the property) as the key and a tuple with getter delegate and setter delegate for each of the property. Now your ChangeColor method could look like this:

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

The code would be more readable if you used your own type with Get property and Set property instead of Tuple with properties named Item1 and Item2. This solution may be useful in some scenarios, but you still need to explicitly list all the properties when initializing the dictionary.

铜锣湾横着走 2024-09-02 13:23:34

这可能正是您所寻找的,但您可能想添加一些错误处理。
它适用于任何类型的公共财产;并设置;方法。
如果你愿意的话,有一些方法可以减少“魔弦”的使用。

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

This might be what your looking for, you may want to add some error handling though.
It will work with any kind of property with public get; and set; methods.
And if you want to there is ways to reduce use of "magic-strings".

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);
}
酸甜透明夹心 2024-09-02 13:23:34

好吧,由于您给出了一个非常简单的示例,所以很难判断到底发生了什么。

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

现在,除了要做什么之外,操作是否相同?

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

您有一些选择:

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

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

Well, it's kind of hard to tell what's really going on since you've given a very simplified example.

But, what I'm really reading is that you want to have a method that will perform one of a number of possible modifications to local state based upon one of the parameters of the method.

Now, is the operation the same, except for what it's being done to?

Ultimately, you have to have some code that understandds that maps an input to a desired operation. How much that can be generalized depends upon how similar the actions are (if it's always 'add 5 to a property' you have more generalization options...).

Some options you have are:

  1. Write a class which encapsulates the Color struct.
  2. Use a lookup table of Actions, as suggested by Kev Hunter.
  3. Write a switch statement.
  4. Pass in a parameter which contains a virtual method which can be executed on the internal data (or just pass in an Action<> directly) - avoiding the lookup

And... that's about it, really. Which one of these makes the most sense probably depends more on your actual use case (which we don't really have a lot of info on) than anything else.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文