Author Topic: Using the Generic KeyValuePair<>  (Read 11037 times)

0 Members and 1 Guest are viewing this topic.

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8908
  • AKA Daniel
Using the Generic KeyValuePair<>
« on: January 14, 2007, 03:20:45 AM »
I thought I might give an example of this. I found this after building my own struct to hold a key pair (Good practice though) .
The built-in KeyValuePair<> struct provides most of the basic methods needed such as:

Code: [Select]
Equals()
GetHashCode()
GetType()
{get, set ;}Key()
ToString()
{get, set ;}Value()

Code - C#: [Select]
  1. using System;
  2. using System.Collections.Generic;
  3. using Autodesk.AutoCAD.Runtime;
  4. //
  5. using AcEd = Autodesk.AutoCAD.EditorInput;
  6. using AcadApp = Autodesk.AutoCAD.ApplicationServices.Application;
  7.  
  8. [assembly: CommandClass(typeof(MyClassLibrary.CRPClass))]
  9.  
  10. namespace MyClassLibrary
  11. {
  12.   public class CRPClass
  13.   {
  14.     private static short myTypecode;
  15.  
  16.     public CRPClass()
  17.     {
  18.     }
  19.     [CommandMethod("gen")]
  20.     static public void test()
  21.     {
  22.       //
  23.       AcEd.Editor ed = AcadApp.DocumentManager.MdiActiveDocument.Editor;
  24.  
  25.       //build a list that uses the built-struct KeyValuePair<>;
  26.       List<KeyValuePair<short, object>> myList = new List<KeyValuePair<short, object>>();
  27.       //add some stuff to the list
  28.       myList.Add(new KeyValuePair<short, object>(1000, (object)"Object0"));
  29.       myList.Add(new KeyValuePair<short, object>(1001, (object)"Object1"));
  30.       myList.Add(new KeyValuePair<short, object>(1002, (object)"Object2"));
  31.       myList.Add(new KeyValuePair<short, object>(1003, (object)"Object3"));
  32.       myList.Add(new KeyValuePair<short, object>(1004, (object)"Object4"));
  33.  
  34.       //print the list
  35.       for (int i = 0; i < myList.Count; i++)
  36.       {
  37.         ed.WriteMessage("\n" + myList[i].ToString());
  38.       }
  39.  
  40.       //find using our delegate below
  41.       object Nth1 = nth1(myList, 1001);
  42.       ed.WriteMessage("\n.\nReturn: find 1001 [" + Nth1.ToString() + "]");
  43.  
  44.     }
  45.     //
  46.     internal static object nth1(List<KeyValuePair<short, object>> list, short crpType)
  47.     {
  48.       myTypecode = crpType;
  49.       return (object)list.Find(finder).Value;
  50.     }
  51.     //helper function
  52.     internal static bool finder(KeyValuePair<short, object> i)
  53.     {
  54.       if (i.Key.Equals(myTypecode))
  55.       {
  56.         return true;
  57.       }
  58.       return false;
  59.     }
  60.     //
  61.   }
  62. }
  63.  

Sometimes it pays to look around before rolling your own.

edit:kdub--> code=csharp
« Last Edit: October 01, 2012, 01:00:46 AM by Kerry »

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11654
  • class keyThumper<T>:ILazy<T>
Re: Using the Generic KeyValuePair<>
« Reply #1 on: January 14, 2007, 04:23:30 AM »
That's the one I was after !
Thought about using it for property lists.

Thanks Daniel.
kdub, kdub_nz in other timelines.
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

Glenn R

  • Guest
Re: Using the Generic KeyValuePair<>
« Reply #2 on: January 30, 2007, 07:15:19 PM »
Any reason why you didn't use this:

Code: [Select]
Dictionary<short, yourObjectTypeHere> someVar = new Dictionary<short, yourObjectTypeHere>();

...as the Dictionary uses a KeyValuePair anyway to store it's mojo...

Just curious.

Cheers,
Glenn.

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8908
  • AKA Daniel
Re: Using the Generic KeyValuePair<>
« Reply #3 on: March 07, 2007, 04:57:04 AM »
Any reason why you didn't use this:

Code: [Select]
Dictionary<short, yourObjectTypeHere> someVar = new Dictionary<short, yourObjectTypeHere>();

...as the Dictionary uses a KeyValuePair anyway to store it's mojo...

Just curious.

Cheers,
Glenn.

Well .. because Iím a tenderfoot and I didnít see it?
I think I am going to have to roll my own structs anyway, I need to store an association list thatís collected and returned to/from Xdata.

(short)DxfCode.ExtendedDataInteger16
(short)MyKey
(short) DxfCode.ExtendedDataÖ
(object)MyValue

This what I came up with. Any better ideas?


Code - C#: [Select]
  1. public struct XdataStruct
  2.   {
  3.     //
  4.     private short m_xdata1;
  5.     private short m_crptype;
  6.     private short m_xdata2;
  7.     private object m_crpvalue;
  8.     //This is slower Box/UnBox
  9.     public XdataStruct(object[] arrayList)
  10.     {
  11.       if (arrayList == null)
  12.       {
  13.         throw new ArgumentNullException("arrayList");
  14.       }
  15.       else
  16.       {
  17.         m_xdata1 = (short)arrayList[0];
  18.         m_crptype = (short)arrayList[1];
  19.         m_xdata2 = (short)arrayList[2];
  20.         m_crpvalue = (object)arrayList[3];
  21.       }
  22.     }
  23.     public XdataStruct(ArrayList arrayList)
  24.     {
  25.       if (arrayList == null)
  26.       {
  27.         throw new ArgumentNullException("arrayList");
  28.       }
  29.       else
  30.       {
  31.         m_xdata1 = (short)arrayList[0];
  32.         m_crptype = (short)arrayList[1];
  33.         m_xdata2 = (short)arrayList[2];
  34.         m_crpvalue = (object)arrayList[3];
  35.       }
  36.     }
  37.     //This is faster
  38.     public XdataStruct(short extData1, short crpType, short extData2, object crpValue)
  39.     {
  40.       m_xdata1 = extData1;
  41.       m_crptype = crpType;
  42.       m_xdata2 = extData2;
  43.       m_crpvalue = crpValue;
  44.     }
  45.     public short XData1
  46.     {
  47.       get
  48.       {
  49.         return m_xdata1;
  50.       }
  51.       set
  52.       {
  53.         m_xdata1 = value;
  54.       }
  55.     }
  56.     public short CrpType
  57.     {
  58.       get
  59.       {
  60.         return m_crptype;
  61.       }
  62.       set
  63.       {
  64.         m_crptype = value;
  65.       }
  66.     }
  67.     public short XData2
  68.     {
  69.       get
  70.       {
  71.         return m_xdata2;
  72.       }
  73.       set
  74.       {
  75.         m_xdata2 = value;
  76.       }
  77.     }
  78.     public object CrpValue
  79.     {
  80.       get
  81.       {
  82.         return m_crpvalue;
  83.       }
  84.       set
  85.       {
  86.         m_crpvalue = value;
  87.       }
  88.     }
  89.     public static bool operator ==(XdataStruct XdataStructA, XdataStruct XdataStructB)
  90.     {
  91.       return ((((XdataStructA.m_xdata1 == XdataStructB.m_xdata1) &&
  92.                 (XdataStructA.m_crptype == XdataStructB.m_crptype)) &&
  93.                 (XdataStructA.m_xdata2 == XdataStructB.m_xdata2)) &&
  94.                 (XdataStructA.m_crpvalue == XdataStructB.m_crpvalue));
  95.     }
  96.     public static bool operator !=(XdataStruct XdataStructA, XdataStruct XdataStructB)
  97.     {
  98.       return !(XdataStructA == XdataStructB);
  99.     }
  100.     public override bool Equals(object obj)
  101.     {
  102.       if (obj is XdataStruct)
  103.         return this == (XdataStruct)obj;
  104.       else
  105.         return false;
  106.     }
  107.     public override int GetHashCode()
  108.     {
  109.       return base.GetHashCode();
  110.     }
  111.     public string ToString(IFormatProvider provider)
  112.     {
  113.       object[] objArray1 = new object[] { this.m_xdata1, this.m_crptype, this.m_xdata2, this.m_crpvalue };
  114.       return string.Format(provider, "({0},{1},{2},{3})", objArray1);
  115.     }
  116.     public override string ToString()
  117.     {
  118.       return ToString(null);
  119.     }
  120.   }
  121.   #endregion
  122.  

edit:kdub--> code=csharp
« Last Edit: October 01, 2012, 01:01:31 AM by Kerry »

TonyT

  • Guest
Re: Using the Generic KeyValuePair<>
« Reply #4 on: March 09, 2007, 08:36:51 AM »
Hi Dan.

It helps to explore the new classes in .NET 2.0, because
a lot of things have changed with the introduction of
generics (like the Dictionary class, for example).

In the case of XData, the ResultBuffer and TypedValue
objects are the managed storage medium.

One problem there, is that its hard to incrementally build
an array of TypedValue[], because arrays don't allow you
to add elements that easily.

To deal with that, I use something like this:

Code - C#: [Select]
  1.  
  2. public class TypedValueList : List<TypedValue>
  3. {
  4.    public TypedValueList( params TypedValue[] args )
  5.    {
  6.       AddRange( args );
  7.    }
  8.  
  9.    // Make it a bit easier to add items:
  10.  
  11.    public void Add(int typecode, object value)
  12.    {
  13.       base.Add(new TypedValue(typecode, value));
  14.    }
  15.  
  16.    // Implicit conversion to SelectionFilter
  17.    public static implicit operator SelectionFilter( TypedValueList src )
  18.    {
  19.       return src != null ? new SelectionFilter( src ) : null;
  20.    }
  21.  
  22.    // Implicit conversion to ResultBuffer
  23.    public static implicit operator ResultBuffer( TypedValueList src )
  24.    {
  25.       return src != null ? new ResultBuffer( src ) : null;
  26.    }
  27.  
  28.    // Implicit conversion to TypedValue[]
  29.    public static implicit operator TypedValue[]( TypedValueList src )
  30.    {
  31.       return src != null ? src.ToArray() : null;
  32.    }
  33.  
  34.    // Implicit conversion from TypedValue[]
  35.    public static implicit operator TypedValueList( TypedValue[] src )
  36.    {
  37.       return src != null ? new TypedValueList( src ) : null;
  38.    }
  39.  
  40.    // Implicit conversion from SelectionFilter
  41.    public static implicit operator TypedValueList( SelectionFilter src )
  42.    {
  43.       return src != null ? new TypedValueList( src.GetFilter() ) : null;
  44.    }
  45.  
  46.    // Implicit conversion from ResultBuffer
  47.    public static implicit operator TypedValueList( ResultBuffer src )
  48.    {
  49.       return src != null ? new TypedValueList( src.AsArray() ) : null;
  50.    }
  51.  
  52. }
  53.  
  54. // Example:
  55.  
  56.    TypedValueList values = new TypedValueList();
  57.  
  58.    values.Add( 1001, "myappname");
  59.    values.Add( 1040, 99.99 );
  60.    values.Add( 1070, 25);        //......
  61.  
  62.    // a method that takes a result buffer
  63.    public void foo(ResultBuffer buffer)
  64.    {
  65.    }
  66.  
  67.    foo(values);  // can pass a TypedValueList anywhere that
  68.                      // ResultBuffer or SelectionFilter is required
  69.  
  70.    // method that returns a ResultBuffer:
  71.    public ResultBuffer bar()
  72.    {
  73.         return new ResultBuffer(...);  
  74.    }
  75.  
  76.    // Direct assignment from ResultBuffer:
  77.    TypedValueList values = bar();    
  78.  
  79.  

edit:kdub--> code=csharp
« Last Edit: October 01, 2012, 01:01:58 AM by Kerry »

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8908
  • AKA Daniel
Re: Using the Generic KeyValuePair<>
« Reply #5 on: March 09, 2007, 10:04:55 AM »
Wow! Thanks a ton Tony! It seems I am missing a lot of stuff here

Iím using my lisp brain here so Iím probably going about this totally wrong. What I need to do is store an association list like (( 2000 , 724.00)) where 2000 would be my lookup key and 724.00 is the value. I addition to storing my data, I was thinking that I should also store the Xdata type as well, for example ((1070 , 2000 , 1040 , 724.00) (1070 , 2001 , 1071 , 9999)) since my value will be of different types. I probably donít need to store the first Xdata type since my key will always be an Int16. I was hoping that by storing the Xdata type I wouldnít have to guess at it later.

 Instead of using the TypedValue struct I thought I would make a new struct ďXdataStruct ď that would contain the four items and I would use the List <XdataStruct> collection to store it all.

I could then use List.Find (Predicate<T> match) to retrieve the value I need.

Assuming that I am doing this correctly, my concern is if my struct is too big. I have read that it should be kept at or under 16 bytes. By using 3 Int16s and an Object, Iím exceeding this. I could drop the first short though.

Am I going about this correctly?

Thanks

TonyT

  • Guest
Re: Using the Generic KeyValuePair<>
« Reply #6 on: March 09, 2007, 10:55:26 AM »
Without knowing very much about your particulars, I'd only be
guessing, but the functional equivalent to an association list
in .NET would be the Dictionary<Key,Value> that is, presuming
the Key is unique for each element.

Hence, if your lookup key is an int (or short):

  public class MyDataDictionary : Dictionary<Int16, TypedValue> {}

You can of course, use your own data structure as well, rather
than the TypedValue struct.


Wow! Thanks a ton Tony! It seems I am missing a lot of stuff here

Iím using my lisp brain here so Iím probably going about this totally wrong. What I need to do is store an association list like (( 2000 , 724.00)) where 2000 would be my lookup key and 724.00 is the value. I addition to storing my data, I was thinking that I should also store the Xdata type as well, for example ((1070 , 2000 , 1040 , 724.00) (1070 , 2001 , 1071 , 9999)) since my value will be of different types. I probably donít need to store the first Xdata type since my key will always be an Int16. I was hoping that by storing the Xdata type I wouldnít have to guess at it later.

 Instead of using the TypedValue struct I thought I would make a new struct ďXdataStruct ď that would contain the four items and I would use the List <XdataStruct> collection to store it all.

I could then use List.Find (Predicate<T> match) to retrieve the value I need.

Assuming that I am doing this correctly, my concern is if my struct is too big. I have read that it should be kept at or under 16 bytes. By using 3 Int16s and an Object, Iím exceeding this. I could drop the first short though.

Am I going about this correctly?

Thanks


It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8908
  • AKA Daniel
Re: Using the Generic KeyValuePair<>
« Reply #7 on: March 10, 2007, 10:19:42 PM »
 public class MyDataDictionary : Dictionary<Int16, TypedValue> {}

Way cool Tony, I think this what I am looking for.
Thanks

kdub_nz

  • Mesozoic keyThumper
  • SuperMod
  • Water Moccasin
  • Posts: 2164
  • class keyThumper<T>:ILazy<T>
Re: Using the Generic KeyValuePair<>
« Reply #8 on: April 30, 2024, 12:50:30 AM »
Update:
If anyone hits this page  ( now 17 years old )
Quote
   /// Over the years, new functionality has been
   /// added, implemented as extension methods.
Refer to :
TypedValueListExtensions.cs
and
TypedValueList.cs
in https://github.com/ActivistInvestor/AcMgdUtility/tree/main/TypedValueHelpers

Regards,


edit:kerry - amend link for repo changes.
« Last Edit: May 01, 2024, 06:40:48 PM by kdub_nz »
Called Kerry in my other life
Retired; but they dragged me back in !

I live at UTC + 13.00

---
some people complain about loading the dishwasher.
Sometimes the question is more important than the answer.

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8908
  • AKA Daniel
Re: Using the Generic KeyValuePair<>
« Reply #9 on: April 30, 2024, 09:04:35 AM »
I remember that, CRP was short for crap.... when did I start using doit?  :lmao:

Atook

  • Swamp Rat
  • Posts: 1031
  • AKA Tim
Re: Using the Generic KeyValuePair<>
« Reply #10 on: May 01, 2024, 12:51:58 AM »
Woah, I just discovered Tony's github..

Thanks Tony!




retsameht

  • Newt
  • Posts: 22
Re: Using the Generic KeyValuePair<>
« Reply #11 on: May 10, 2024, 05:38:04 AM »
Just a heads up: Don't use the code in this file until I fix it:

https://github.com/ActivistInvestor/AcMgdUtility/blob/main/DocumentCollectionExtensions.cs

UPDATE 5/10/24: Bugs fixed (WaitForIdle(), and WaitUntil() were hanging AutoCAD :oops:).

WaitForIdle() is a solution to a problem that has irked me for a long time (handling the next Application.Idle event). I had previously dealt with it using a class that wraps the handler for the event, but that still required the code to be run in the Idle event handler to be passed in a delegate.

WaitForIdle() is a far-simpler and better solution that requires nothing but an awaited method call, followed by the code that should run after the Idle event is raised.

Code - C#: [Select]
  1. /// WaitForIdle():
  2.  
  3. public static async void MyMethod()
  4. {
  5.    var DocMgr = Application.DocumentManager;
  6.    
  7.    // wait for an Idle event to be raised:
  8.    
  9.    await DocMgr.WaitForIdle();
  10.    
  11.    // Code appearing here will not run until
  12.    // the next Idle event is raised and there
  13.    // is an active document.
  14.    
  15.    var doc = DocMgr.MdiActiveDocument;      
  16.    doc.Editor.WriteMessage("An Idle event was raised.");
  17. }
  18.  
  19.  
  20. /// WaitUntil():
  21.  
  22. public static async void MyMethod()
  23. {
  24.    var DocMgr = Application.DocumentManager;
  25.    
  26.    // Waits until there is an active document
  27.    // that is in a quiescent state:
  28.    
  29.    await DocMgr.WaitUntil(doc => doc.Editor.IsQuiescent);
  30.    
  31.    // Code appearing here will not run until
  32.    // the next Idle event is raised; there is
  33.    // an active document; and that document
  34.    // is in a quiescent state.
  35.    
  36.    var doc = DocMgr.MdiActiveDocument;      
  37.    doc.Editor.WriteMessage("The Drawing Editor is quiescent");
  38. }
  39.  
  40.  
« Last Edit: July 08, 2024, 04:11:10 AM by retsameht »

retsameht

  • Newt
  • Posts: 22
Re: Using the Generic KeyValuePair<>
« Reply #12 on: July 08, 2024, 04:12:38 AM »
UPDATE: 7/8/24: The above solution was abandoned after discovering that AutoCAD does not like async/await at all. There is a fundamental problem with using async/await in AutoCAD, which is that any exception that is not handled will terminate the process.

For example, this will terminate AutoCAD:

Code - C#: [Select]
  1.  
  2.    await DocMgr.WaitForIdle();
  3.    throw new NotSupportedException("Adios");
  4.  
  5.  

The problem has nothing to do with the WaitForIdle() implementation. It happens with any await'ed call to any async method.

You can still use WaitForIdle() or an await'ed call to any other async method, but you must always wrap the call in a try block, and if an exception is caught do not re-throw it.
« Last Edit: July 08, 2024, 04:17:13 AM by retsameht »