2

I want to tag the values of variables with a "quality" attribute that can be changed dynamically during my programs execution. My first thought was to abuse the standard attribute functionality by tagging the value with my custom quality attribute. EG

[QualityAttribute(Quality.Bad)]
int value = 0;
...
value = 1;
(** somehow set QualityAttribute of value to Quality.Good **)

But from what I have seen attributes were intended as a static measure and are baked into meta-data, making what I want to do apparently impossible.

An alternative would be to make a struct/class that contains both the variable and the quality. EG

class Value
{
  public Quality quality = Quality.Bad;
  public int value = 0;
}
...
Value value = new Value();
value.value = 1;
value.quality = Quality.Good;

This will work (and is similar to what is used to support nullable types) but I don't really like the extra layer of indirection to get to the value.

Is there any other way that allows direct access to the value and easily supports tagging value with a custom, dynamically alterable attribute?

  • 2
    use a struct with a conversion to contained type? – Caleth Jul 6 '17 at 12:34
  • @Caleth I assume that you would mean adding a second property to hold the quality (which would then have to be explicitly accessed)? – Peter M Jul 6 '17 at 12:57
  • Yes, your class Value, but with an operator int method – Caleth Jul 6 '17 at 13:05
  • @Caleth You should post your comment as an answer – Peter M Jul 6 '17 at 13:28
  • 1
    Stop using attributes. They are poor OO, and often cause problems. You should use a Monad as described in CandiedOrange's anwser. – TheCatWhisperer Jul 6 '17 at 16:11
0

This is basically a expansion over @CandiedOrange's answer, go pop an upvote to them if you end up liking this.

Why not implementing something similar to a nullable?

Enters the "Qualitable" class:

public class Qualitable<T>
{
    T Value { get; set; }
    QualityEnum Quality;  

    public Qualitable(T Value, QualityEnum Quality)
    {
        this.Value = Value;
        this.Quality = Quality;
    }
}

Then, you can just do this on your code:

Qualitable<int> myThing = new Qualitable<int>(10, QualityEnum.VeryGood)

if(myThing.Quality == QualityEnum.VeryGood)
{
    DoSomething(myThing.Value);
    //Pop an upvote 
}
else
{ 
    DoSomethingElse(myThing.Value);
   //Do it anyway, me tries hard :(
}
  • 2
    This is basically exactly what the OP suggested and wanted to avoid. – Derek Elkins Jul 6 '17 at 21:10
  • @DerekElkins Well the OP went ahead and ignored all the Monad discussion (as a lot of it was over my head anyway) and circled back to basically this same idea. But I also rolled in the implicit conversion idea mentioned by @Caleth. In hind sight I think this is about as good as it gets for simplicity and practicality and understand-ingness. – Peter M Jul 6 '17 at 21:51
  • On the other hand, I do think the OP should just live with having to add .Value. Your answer is a cleaned-up version of the OP's code, though I'm assuming the OP just meant that code as a sketch. Whether a generic type is appropriate depends on whether these "Qualities" will be used for other types/variables. (Note, that this type is not a monad which, if this was the intent, is why CandiedOrange's answer is not useful.) – Derek Elkins Jul 6 '17 at 21:53
  • @PeterM Yeah, the implicit conversion approach does seem to address your problem, though I'm not sure it should be solved. I strongly prefer things to be explicit, though, so I'm definitely biased. – Derek Elkins Jul 6 '17 at 21:57
  • @DerekElkins Well I implemented it during the day and when I went to actually use it I realized that I didn't really need the implicit conversion. – Peter M Jul 6 '17 at 22:20
2

I like the struct/class alternative that contains both the variable and the quality. I don't like to use static stuff without a darn good reason. Except I also don't like squishing together two different abstractions. Meta info simply isn't at the same level as what it describes.

This is screaming "use a monadic style" to me.

You can nest these and put value at one level and quality at another. Or keep quality as a property on what's basically a generic container.

This means your value can be anything and isn't tied to your quality interface. You can swap types as needed.

You might be about to complain that this isn't direct but look up the proper way to access a monad and ask yourself if this isn't better.


This answer is sparking some controversy so let me explain it a little more without drowning it in detail.

The main thing I'm looking for in this design is to not force branching.

If this is just a struct or a pile of getters then users are forced to test quality and make a decision based on that. What would be nice is if that logic were pushed down and abstracted away so that good or bad you get the value you need when you ask for it.

Whether that means adding methods like UnlessBad(T other) or something else I'm not entirely sure. Just thought it worth considering.


public Quality<Integer> qualityAdd(Quality<Integer> val1, Quality<Integer> val2) {
    return
        val1.flatMap( first =>
        val2.flatMap( second =>
        Quality.of(first + second)
    ));
}

This will add the int's together to make a new good Quality provided both were of good quality. Otherwise a bad Quality is returned. This is a short chain but there is no set limit to it.

Done this way all the checking for quality is hidden. When bad quality is detected the chain can be stopped by simply not calling any more lambdas and returning a bad quality. Alternatively, all int's can be summed and quality can be OR'ed on the way back from the return. Either way, one bad would turn all bad.

0

Attributes are static and cannot changed at runtime, so it's kind of a dead end. You may want to look at the Type Descriptor, it may solve your problem...

https://blogs.msdn.microsoft.com/parthopdas/2006/01/04/understanding-the-typedescriptor-a-metadata-engine-for-designtime-code/

https://msdn.microsoft.com/en-us/library/system.componentmodel.typedescriptor.aspx

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.