locked
Using fields as parameters in a method RRS feed

  • Question

  • I have a requirement in a project to build a history. No biggie, but there are dozens of bool fields and processing each one is very time consuming.

    My approach is to add a field name to an arraylist when the property is changed. OnSave() or Dispose() I look into the object and check to see if the field list is empty or not. If not, I process the list looking at each item comparing it to a clone of the original object so that only true changes are written to the child history collection:
    internal void WriteHistory(){
         string changedValue = string.Empty;
         originalValue=string.Empty;

         if
    (ChangedItems.Contains("IsDesignProject"))
         {
              i
    f(Original.IsDesignProject != this.IsDesignProject && this.IsDesignProject == false)
              {
                   changedValue = "False";
                   originalValue = "True";
              }
              else
              {
                   changedValue = "True";
                   originalValue = "False";
              }
              HistoryItem historyItem = HistoryItem.NewHistoryItem(
    this.Identity.FullName, changedValue, originalValue, "IsDesignProject", this.ID);
    this.HistoryItems.Add(historyItem);
         }
    }

    Is there a way to use a parameter (string) in the constructor so that the filed name can be passed into the method and reduce the code to one generic method, for example:
    internal void WriteBoolHistory(string fieldName){
    ...
    if(Original.fieldName != this.fieldName

    I don't think a strongly typed object can do this sort of thing, but I thought I'd ask and possibly improve/shorten my code base.

    Thanks in advance,

    Friday, June 2, 2006 1:48 PM

Answers

  • A small example, you have class with up to 32 bool properties. You declare single int field where bits will be stored (or use BitVector, which is the same basically). Then you write properties that access different bits. You need to save/load only one int to save/restore all properties. If you have 32-64 bool properties - use long instead.

    Should be something like this:

     

    class ManyProps

    {

    int Values = 0; // All set to false

    bool Get(int index) { return (Values & (1 << index)) != 0; }

    void Set(int index, bool value)

    {

    if (value)

    Values |= (1 << index);

    else

    Values &= ~(1 << index);

    }

    public bool Prop1

    {

    get { return Get(0); }

    set { Set(0, value); }

    }

    public bool Prop2

    {

    get { return Get(1); }

    set { Set(1, value); }

    }

    }

    Friday, June 2, 2006 2:18 PM

All replies

  • Hi!

    Can you use bits instead? For example System.Collections.Specialized.BitVector32 class can hold up to 32 bool values and took only 4 bytes of memory.

    You can use int or long to keep 32/64 bits. You can keep int that contain current values, int that contain changed bits mask. Using bit operators you can do all checks in single call and make it really fast.

     

    Also you have to choose what do you need - short and slow code or long and fast code. You can do very simple "generic" method with reflection, but it will be slow.

    Friday, June 2, 2006 1:54 PM
  • Thanks, Sergey, but how would they be translated back to the database? As an array of values? I've not used bits this way before. My object reads its data from the db into the fields. I take it the array would be mapped to the fields I need? How does binding binding work? I didn't think binding a control to a specific value in an array was possible... hmm.

    Friday, June 2, 2006 2:03 PM
  • A small example, you have class with up to 32 bool properties. You declare single int field where bits will be stored (or use BitVector, which is the same basically). Then you write properties that access different bits. You need to save/load only one int to save/restore all properties. If you have 32-64 bool properties - use long instead.

    Should be something like this:

     

    class ManyProps

    {

    int Values = 0; // All set to false

    bool Get(int index) { return (Values & (1 << index)) != 0; }

    void Set(int index, bool value)

    {

    if (value)

    Values |= (1 << index);

    else

    Values &= ~(1 << index);

    }

    public bool Prop1

    {

    get { return Get(0); }

    set { Set(0, value); }

    }

    public bool Prop2

    {

    get { return Get(1); }

    set { Set(1, value); }

    }

    }

    Friday, June 2, 2006 2:18 PM
  • That does seem to be a good consolidation for MANY of my projects where I don't need history, thank you!

    I still have the problem of getting the field name into the constructor for the history item. From your example class, I would want to write something to include the property name. An attribute on the property might do it. This application will not need to traverse firewalls so reflection might be useful here.

    What do you think?

    Friday, June 2, 2006 2:31 PM
  • History can store int Value with changed fields bit mask.

     

    int oldValue;

    int newValue;

    int changedFieldsMask = oldValue ^ newValue;

     

    changedFIeldMask will keep bits that was changed to 1 and others will be 0. Only 1 int to store all changes in history. History will be much more compact and effective.

    Friday, June 2, 2006 2:37 PM
  •  Sergey Galich wrote:

    changedFIeldMask will keep bits that was changed to 1 and others will be 0. Only 1 int to store all changes in history. History will be much more compact and effective.


    Unfortunately, my users need to have everything spelled out for them, so it would need to be stuffed into a text format. BUT using the changedFieldMask would allow a method to find only changes and process them quickly. Good idea. I could also keep the field name in a read only collection in the object built with the property in mind. Maybe use an enum to parse the indexer and return a string value from the enum declaration.

    This sounds promising. Thank you very much!

    Friday, June 2, 2006 2:50 PM
  • It sounds to me like you're asking if you can retrieve a field or property of an object by its name which is represented as a string at runtime?  If so, then you can do it with reflection.  To get a field of an object of a certain name, use something like object.GetType().GetField(fieldName, BindingFlags.Public | BindingFlags.Instance).GetValue(object).  Properties work similarly: object.GetType().GetProperty(fieldName, BindingFlags.Public | BindingFlags.Instance).GetValue(object).  Look at the documentation for more detail about these methods.  In particular, look at the BindingFlags enumeration, as the values I give in my example will not always be correct, depending upon the situation.

    Friday, June 2, 2006 3:03 PM
  • Thanks, Nimrand, but no GUI bindings will be available to it since it's in the business layer and unaware of the GUI. Reflection does bring in the field names but I don't want to rely on Reflection. This project may need to be a public web app in which case all the reflection would need to be stripped out.

    I found out that last part when my manager was reading the post over my shoulder. Nice to have such dynamic specifications. <grrr>

    Friday, June 2, 2006 3:10 PM
  • Um, I think you're confused about what reflection is, actually.  Windows Forms data binding USES reflection, but reflection is a whole different API unto itself that has no dependency on windows forms data binding.  Nothing I posted has anything to do with GUI binding. 

    Reflection is just an API that allows you to inspect an object's members dynamically at runtime.  For instance, one can get a list of all the properties a given object supports without knowing what type of object it is.

    So, why would you need to remove reflection if you made it a public web application?

    And just to clarify any confusion, the BindingFlags used by the reflection API are basically just a set of filters that one can apply when retrieving members of an object.  Despite their name, they have nothing to do with data binding as used in .NET's GUI APIs.

    Friday, June 2, 2006 3:42 PM
  • I was referring to using the reflection namespace in a remoting context which is where the app would go. It gets blocked as unsafe code even if it's a trusted source. (Don't ask me why.)

    As far a reflection goes, it makes more sense now that I had it defined from a grammatical point of view. Reflexive as oneself. Like shaving oneself is a reflexive action. My English is still improving.

    Friday, June 2, 2006 3:52 PM
  • Ah, now I understand better.  You're performing reflection on the remote server in a remoted object, but it gets blocked even when the DLL has full trust.  If thats right, then its important to note that when performing security checks, .NET requires that the currently running assembly and all assemblies in the call stack to have the needed permission in order to proceed.  In this case, .NET may be including the client in the call stack, and it would probably not trust the client at all, and so would block the reflection because it looks like something is being done on behalf of an untrusted client that is potentially unsafe.  If so, then you can use the SecurityPermission.Assert method to override this behavior.  I don't know if it would be worth the trouble, but I thought I'd throw it out there.
    Friday, June 2, 2006 4:26 PM
  • That's correct. I did demand that security, but that piece of code failed a security audit *because* I did that, so I removed it and stopped using remoting that way. We'll eventually be in the larger domain forest and I think that will make passing security tokens around our apps more reliable. Until then, however, brute force programming is key. <s>

    I'm going to see if the bit array fixes my problem. I sure think it will and I'm going to mark that as the answer to this question.

    Thanks for all the input.

    Friday, June 2, 2006 5:28 PM