I am reading Silverlight 4 in Action and simultaneouly trying examples in it. However, i can't seem to understand the new dynamic keyword and why is it needed for COM. This is my code :-
if (AutomationFactory.IsAvailable)
{
dynamic excel =
AutomationFactory.CreateObject("Excel.Application");
excel.Visible = true;
dynamic workbook = excel.workbooks;
workbook.Add();
dynamic sheet = excel.ActiveSheet;
int i = 1;
double[] data = new double[] { 1.0, 5.0, 9.5, 2.7, 3.2, 0.6 };
foreach (double d in data)
{
dynamic cell = sheet.Cells[i, 1];
cell.Value = "Row " + i;
cell.ColumnWidth = 10;
cell = sheet.Cells[i, 2];
cell.Value = d;
i++;
}
dynamic shapes = sheet.Shapes;
shapes.AddChart(-4100, 120, 2, 300, 200);
}
I am wondering how did the author come to know that shapes support AddChart method and how did he know which parameters he needed to pass to this function? Can somebody throw some light on this?
Thanks in advance :)
It is not needed for COM. However, the code that doesn't use dynamic is a real mess.
As to how the author knew what methods are available, he read the Excel VBA documentation. Looking at the Excel type library would also tell you the method signatures, but without the explanation.
Dynamic basically means you don't know at compile time what type you will have (or sometimes the type doesn't even exist yet). Hence the absence of intellisense.
With a dynamic object you just call a method, throw in some parameters, and if that exact method with that signature exists, the DLR will call it.
If it doesn't exist you'll get a runtime exception.
" method and how did he know which parameters he needed to pass to this function? "
He probable coded it or read the documentation.
Related
I use Python.Net for C# interaction with Python libraries. I solve the problem of text classification. I use FastText to index and get the vector, as well as Sklearn to train the classifier (Knn).During the implementation, I encountered a lot of problems, but all were solved, with the exception of one.
After receiving the vectors of the texts on which I train Knn, I save them to a separate text file and then, if necessary, use it.
string loadKnowVec = File.ReadAllText("vectorKnowClass.txt", Encoding.Default);
string[] splitKnowVec = loadKnowVec.Split('\r');
splitKnowVec = splitKnowVec.Where(x => x != "").ToArray();
for()
{
keyValues_vector.Add(float.Parse(splitKnowVec[i], NumberFormatInfo.InvariantInfo), 1);
}
dynamic X_vec = np.array(keyValues_vector.Keys.ToArray()).reshape(-1, 1);
dynamic y_tag = np.array(keyValues_vector.Values.ToArray());
dynamic neigh = KNN(n_neighbors: 3);
dynamic KnnFit = neigh.fit(X_vec, y_tag);
string predict = neigh.predict("0.00889");
MessageBox.Show("Скорее всего это: "+predict);
During the training of the classifier, I encountered such a problem that from c# to python, it is not values with the float type, but the value of System.Single[].
Python.Runtime.PythonException: "TypeError : float() argument must be a string or a number,
not 'Single[]'
The stored value, at this point, of dynamic X_vec is "System.Single[]".(I think that's exactly the problem)
2.At first I tried to manually set the values of X_vec, but the error and its values were the same.
The first idea was to change the array type using the numpy library, but it didn't help, it also gave out "".
dynamic Xx = np.array(X_vec, dtype: "float");
dynamic yY = np.array(y_tag, dtype: "int");
Next, it was tried to create an empty array in advance and load specific values into it before changing the data type, but this also did not work.
Perhaps I do not understand the principle of the formation and interaction of the MSVS19 IDE and the python interpreter.
I solved this issue for a couple of days and each time I thought it was worth reading the documentation on python.net .
As a result, I found a solution and it turned out to be quite banal, it is necessary to represent X_vec not as a float[] , but as a List<float>
List<float> vectors = keyValues_vector.Keys.ToList();
List<int> classTag = keyValues_vector.Values.ToList();
dynamic a = np.array(vectors);
dynamic X_vec = a.reshape(-1, 1);
dynamic y_tag = np.array(classTag);
I'm using C# Interop to get some values from a Worksheet and I get the following error:
Non-invocable member 'Microsoft.Office.Interop.Excel.Range.End' cannot be used like a method.
This is my code:
var wb = (Excel.Workbook)Globals.ThisAddIn.Application.ActiveWorkbook;
var wsEvars = wb.Sheets["Evars"];
var wsProps = wb.Sheets["Props"];
var wsEvents = wb.Sheets["Events"];
var wsListVars = wb.Sheets["List Vars"];
var sheetList = new Excel.Worksheet[] { wsEvars, wsProps, wsEvents, wsListVars };
for (var i = 0; i < sheetList.Length; i++)
{
// I get the error on the line below
var rowLast = sheetList[i].Range["I" + sheetList[i].Rows.Count].End(Excel.XlDirection.xlUp).Row;
}
The thing is that is works if I try as follows:
for (var i = 0; i < sheetList.Length; i++)
{
var rowLast = wsEvars .Range["I" + wsEvars .Rows.Count].End(Excel.XlDirection.xlUp).Row;
}
Am I missing something?
Looks like you found a bug in the C# compiler. The bug is actually present in the workaround, it ought to not compile for the same reasons the first snippet did not. Albeit that it is difficult to definitely claim that this is a bug, the C# language spec does not describe what is acceptable in this case.
The Range.End property is an indexed property. Such properties are not formally supported in C#, the language permits only the class indexer (aka this[]) to be the one-and-only indexed property of a class. But that restriction was lifted in C# version 4, specifically to make interop with COM servers easier. Like Excel, indexed properties are very common in COM object models.
Like the normal indexer, you have to use square brackets. Fix:
var rowLast = sheetList[i].Range["I" + sheetList[i].Rows.Count]
.End[Excel.XlDirection.xlUp].Row;
And the workaround you had to use in older C# versions is still available:
var rowLast = sheetList[i].Range["I" + sheetList[i].Rows.Count]
.get_End(Excel.XlDirection.xlUp).Row;
Hard to guess why it finds () parentheses acceptable in the second snippet. It looks like a bug, swims like a bug and quacks like a bug, so it is probably a bug. Let them know about it by clicking the New Issue button. I doubt they'll fix it but there might be more wrong than meets the eye.
Its my first experience with excel file using C#.
As a demo, I wrote a single line in excel file and it worked but now I am trying to insert all the rows of DataTable but it gives the following error:
"HRESULT: 0x800A03EC"
Here is the code:
for (int i = 0; i < table.Rows.Count; i++)
{
xlWorkSheet.Cells[i, 1] = table.Rows[i]["Ref"].ToString();
xlWorkSheet.Cells[i, 2] = table.Rows[i]["Name"].ToString();
}
Please tell me how can I fix it?
Use Range.Value instead. Worksheet.Cells property is readonly:
for (int i = 0; i < table.Rows.Count; i++)
{
xlWorkSheet.Cells[i, 1].Value = table.Rows[i]["Ref"].ToString();
xlWorkSheet.Cells[i, 2].Value = table.Rows[i]["Name"].ToString();
}
Instead of a DataTable, put all the values into a object[,]
Then you can assign them in one swoop:
using (var targetRangeHeader = _excelApp.Range[TargetRangeTopAddress].WithComCleanup())
using (var targetRangeFirstDataCell = targetRangeHeader.Resource.Offset[1, 0].WithComCleanup())
using (var targetRange = targetRangeFirstDataCell.Resource.Resize[MyObjectArrayValues.Length, 1].WithComCleanup())
{
targetRange.Resource.Value2 = MyObjectArrayValues;
Using http://jake.ginnivan.net/vsto-com-interop
I wholeheartedly endorse Jeremy Thompson's answer to use a 2-dimensional object array. The performance will be orders of magnitude faster. I'm writing to explain why the code you've written doesn't work (and why you probably thought it should work).
In Visual Basic, there's a concept of "Default" property. When you write this in VBA, you're using that concept:
xlWorkSheet.Cells(i, 1) = someValue
The Cells property returns a Range object. In VBA, since you haven't used the Set keyword, this line is not reassigning the value of the object (and, if you added the Set keyword, it would fail to reassign the value of the object, since it's a read-only property, as Ria notes).
The absence of the Set keyword causes the assignment to go to the default prpoperty of the Range object returned by Cells(i, 1). The default property, of course, is the Value property. The above VBA sample, in other words, is equivalent to this:
Dim r As Range
Set r = xlWorkSheet.Cells(i, 1)
r.Value = someValue
Of course, you can also skip the r variable:
xlWorkSheet.Cells(i, 1).Value = someValue
Now, C# doesn't have the default property concept. You're therefore unable to write this code the shortcut way, which leads us to the code suggested in Ria's answer:
xlWorkSheet.Cells[i, 1].Value = someValue;
Or, with the real value instead of someValue:
xlWorkSheet.Cells[i, 1].Value = table.Rows[i]["Ref"].ToString();
The default property makes it easy for beginners to write code, and it helps code to be concise, but it's also confusing for people who are between the beginner stage and the advanced stage.
In some languages, almost everything can be used as a value. For example, some languages let you treat a block of code as a unit which can return a value.
In Scheme, a block of code wrapped in a let can return a value:
(define val
(let ()
(define a 10)
(define b 20)
(define c (+ a b))
c))
Perl 5 also supports blocks as values:
my $val = do
{
my $a = 100;
my $b = 200;
my $c = $a + $b;
$c;
};
The closest approximation to block values I could come up with in C# was to use a lambda that is cast and immediately called:
var val = ((Func<int>)(() =>
{
var a = 10;
var b = 20;
var c = a + b;
return c;
}))();
That's not too bad and is in fact exactly what is happening semantically with Scheme; the let is transformed to a lambda which is applied. It's at this point that I wouldn't mind macros in C# to clean up the syntactic clutter.
Is there an another way to get "block values" in C#?
Sorry, at first I seemed to have misread your question, but it seems delegates (as in your question) are exactly what you are looking for no? If you are interested in quickly grouping a set of different values together, and not necessarily logic, my previous answer still applies.
C# supports anonymous types.
var v = new { Amount = 108, Message = "Hello" };
The var keyword is introduced so you don't have to specify a type name.
Afterwards you can access the members as follows:
Console.WriteLine( v.Amount );
Another solution since C# 4.0 is using Tuples which basically group a set of unnamed values together.
var population = Tuple.Create(
"New York", 7891957, 7781984,
7894862, 7071639, 7322564, 8008278 );
You have to access them using population.Item1, population.Item2, ...
In some languages, almost everything can be used as a value.
You give 1 example, Scheme. Scheme is a functional language, (almost) everything in Scheme is a function rather than a value.
C# is now partially a functional language through the inclusion of Linq.
So the equivalents you seek are Linq queries and indeed lambda functions. If that's not enough, take a look at F#.
I have the pleasure to write some code that moves around stuff in an Office XP environment. I've referenced the OfficeXP Interop Assemblies and written code to Search/Replace stuff. That works fine. Now I need to delete Text around a Bookmark and i keep getting Exceptions thrown at me.
Here is some of the code:
object units = WdUnits.wdLine;
object lines = 2;
object extend = WdMovementType.wdExtend;
object bookmarkName = "Bank1";
var bm = doc.Bookmarks;
var bm1 = doc.Bookmarks.get_Item(bookmarkName);
var ra = bm1.Range;
ra.Delete(ref units, ref lines);
The last line is where i get a "Wrong Parameter" Exception. Looking at the Definition in the MSDN I kind of think I'm right. But obviously I'm not. Hope you guys can help me out here.
Update: ok, i see. Using the Delete method on the Range object i can only use wdWord as a Parameter. I'd like to change my question now: what i do want to do is delete two lines starting from the bookmark. How would i do this?
Range objects in Word are not "line oriented", they don't allow line operations, only paragraph operations. However, selections allow line operations. The current selection is not a property of the word document, but of the word application object. Here is some VBA code which does essentially what you try, I think you can easily port this to C#:
Dim rng As Range
Dim doc As Document
Set doc = ActiveDocument
Set rng = doc.Bookmarks("BM").Range
Dim s As Long, e As Long
rng.Select
s = Application.Selection.Start
e = Application.Selection.Next(wdLine, 1).End
Application.Selection.SetRange s, e
Application.Selection.Delete
Ok, i found a way to do what i had to do. Here is the Code:
if (doc.Bookmarks.Exists("Bank1"))
{
object bookmarkName = "Bank1";
object units = WdUnits.wdLine;
object lines = 2;
object extend = WdMovementType.wdExtend;
doc.Bookmarks.get_Item(bookmarkName).Select();
app.Selection.MoveDown(units, lines, extend);
app.Selection.Delete();
}