根据几天前在SO:GetType()和多态性中提出的以下问题并阅读Eric Lippert的回答,我开始思考如果GetType()不是虚拟,确实确保一个对象无法对其产生谎言Type.
具体而言,Eric的答案陈述如下:
框架设计者不会添加一个令人难以置信的危险特性,例如允许对象只是为了使其与同类型的其他三种方法保持一致.
现在的问题是:我是否可以创建一个对其类型有谎言的对象而不会立即明显?我在这里可能是非常错误的,如果是这样的话,我想要澄清,但请考虑以下代码:
public interface IFoo
{
Type GetType();
}
Run Code Online (Sandbox Code Playgroud)
以及所述接口的以下两个实现:
public class BadFoo : IFoo
{
Type IFoo.GetType()
{
return typeof(int);
}
}
public class NiceFoo : IFoo
{
}
Run Code Online (Sandbox Code Playgroud)
然后,如果您运行以下简单程序:
static void Main(string[] args)
{
IFoo badFoo = new BadFoo();
IFoo niceFoo = new NiceFoo();
Console.WriteLine("BadFoo says he's a '{0}'", badFoo.GetType().ToString());
Console.WriteLine("NiceFoo says he's a '{0}'", niceFoo.GetType().ToString());
Console.ReadLine();
}
Run Code Online (Sandbox Code Playgroud)
果然badFoo输出错了Type.
现在我不知道这是否有任何严重影响,基于埃里克将这种行为描述为" 难以置信的危险特征 …
问题很简单.可以改变其内部状态而不能从外部观察的类型是否可以被认为是不可变的?
简化示例:
public struct Matrix
{
bool determinantEvaluated;
double determinant;
public double Determinant
{
get //asume thread-safe correctness in implementation of the getter
{
if (!determinantEvaluated)
{
determinant = getDeterminant(this);
determinantEvaluated = true;
}
return determinant;
}
}
}
Run Code Online (Sandbox Code Playgroud)
更新:澄清线程安全问题,因为它导致分心.
好吧,对于你们中的一些人来说这可能是显而易见的,但我对这个相当简单的代码所得到的行为感到难过:
public static void Main(string[] args)
{
int? n = 1;
int i = 1;
n = ++n - --i;
Console.WriteLine("Without Nullable<int> n = {0}", n); //outputs n = 2
n = 1;
i = 1;
n = ++n - new Nullable<int>(--i);
Console.WriteLine("With Nullable<int> n = {0}", n); //outputs n = 3
Console.ReadKey();
}
Run Code Online (Sandbox Code Playgroud)
我将两个输出都视为相同且等于2但奇怪的是它们不是.有人可以解释原因吗?
编辑:尽管代码生成这个"怪异"的行为固然做作,它看起来像在C#编译器中的错误虽然看似不重要,原因似乎是内联new为詹姆斯最初指出.但行为不仅限于运营.方法调用的行为方式完全相同,也就是说,当它们只应被调用一次时,它们被调用两次.
考虑以下repro:
public static void Main()
{
int? n = 1;
int i = 1;
n = n …Run Code Online (Sandbox Code Playgroud) 我正在继续学习C#和语言规范,这是另一种我不太了解的行为:
C#语言规范在10.4节中明确说明了以下内容:
常量声明中指定的类型必须是sbyte,byte,short,ushort,int,uint,long,ulong,char,float,double,decimal,bool,string,enum-type或reference-type.
它还在4.1.4节中说明了以下内容:
通过const声明,可以声明简单类型的常量(第10.4节).不可能有其他结构类型的常量,但静态只读字段提供类似的效果.
好的,因此使用静态只读可以获得类似的效果.阅读本文后,我去尝试以下代码:
static void Main()
{
OffsetPoints();
Console.Write("Hit a key to exit...");
Console.ReadKey();
}
static Point staticPoint = new Point(0, 0);
static readonly Point staticReadOnlyPoint = new Point(0, 0);
public static void OffsetPoints()
{
PrintOutPoints();
staticPoint.Offset(1, 1);
staticReadOnlyPoint.Offset(1, 1);
Console.WriteLine("Offsetting...");
Console.WriteLine();
PrintOutPoints();
}
static void PrintOutPoints()
{
Console.WriteLine("Static Point: X={0};Y={1}", staticPoint.X, staticPoint.Y);
Console.WriteLine("Static readonly Point: X={0};Y={1}", staticReadOnlyPoint.X, staticReadOnlyPoint.Y);
Console.WriteLine();
}
Run Code Online (Sandbox Code Playgroud)
此代码的输出是:
静态点:X = 0; Y = 0
静态只读点:X = 0; Y = 0
抵销... …
好吧,这是我们偶然发现的一个非常紧张的角落,但它让我很好奇.
请考虑以下代码:
public class Foo
{
private int foo;
public int Reset() => foo = 0; //remember, assignment expressions
//return something!
}
Run Code Online (Sandbox Code Playgroud)
这段代码会编译吗?
不,如果你在所有警告上失败,它就不会; 你会收到member foo is assigned but never used警告.
出于所有目的,此代码与以下内容相同:
public class Foo
{
private int foo;
public int Reset() { foo = 0; return foo; }
}
Run Code Online (Sandbox Code Playgroud)
哪个编译得很好,所以这里有什么问题?请注意,=>语法不是问题,它返回的赋值表达式似乎是混淆了编译器.
好吧,据我所知,不可变类型本质上是线程安全的,所以我在不同的地方读过,我想我明白为什么会这样.如果在创建对象后无法修改实例的内部状态,则对实例本身的并发访问似乎没有问题.
因此,我可以创建以下内容List:
class ImmutableList<T>: IEnumerable<T>
{
readonly List<T> innerList;
public ImmutableList(IEnumerable<T> collection)
{
this.innerList = new List<T>(collection);
}
public ImmutableList()
{
this.innerList = new List<T>();
}
public ImmutableList<T> Add(T item)
{
var list = new ImmutableList<T>(this.innerList);
list.innerList.Add(item);
return list;
}
public ImmutableList<T> Remove(T item)
{
var list = new ImmutableList<T>(this.innerList);
list.innerList.Remove(item);
return list;
} //and so on with relevant List methods...
public T this[int index]
{
get
{
return this.innerList[index];
}
}
public IEnumerator<T> GetEnumerator()
{ …Run Code Online (Sandbox Code Playgroud) 我被检查出的文档PureAttribute在MSDN和我很惊讶,它可以在类级别应用.我知道什么是纯函数,但我从未见过它适用于某种类型.在MSDN文档中,它声明了以下内容:
表示类型或方法是纯的,也就是说,它不会进行任何可见的状态更改.
(用于突出显示的粗体)
所以我的问题是,如果纯类型没有进行任何可见的状态更改,那么它是否与不可变类型相同?如果在类型级别应用这两个术语,是否相同?如果没有,有人可以告诉我一个不可变的纯粹类型的例子或反之亦然.
我正在一个小型教育项目中工作,我们必须实现一个n维矩阵.根据上下文,此矩阵可以使用我们自己的内置ComplexNumber结构,System.Double也可以使用非常简单的示例,使用整数类型(主要是System.Int32).
由于应用程序的性质,我们不需要实现闪电般快速的性能.
因此,我的第一个想法是实行Matrix<T>地方T会以某种方式需要被限制在"数字".
这样做的一个明显问题是,语言中没有办法T用定义的运算符约束泛型类型.此外,我没有看到一种简单的方法来重新T合理到合理的类型.
我的问题是:
有人可以指出我的优雅方式,使用通用类型进行数学运算,不会过多地影响性能,并以某种方式使内置类型工作(如果可能).
如果Eric读过这篇文章,那么这个功能(通过定义的运算符约束泛型类型)是否会出现在C#设计会议的假设未来版本中,并且它是否已经接近将其纳入语言?
我知道实现一个ComplexMatrix类型并为每个矩阵"子类型"(双重,整数等)创建包装器更容易,更好,并支付我们的复杂类型和矩阵元素发生的任何类型之间的转换的性能成本成为.这个问题更多的是出于对某人如何实施类似场景的好奇心.
我正在研究一个简单的数学库用于教育目的,我已经实现了一个struct代表Rational Number的数学库.显示结构核心字段的非常基本的代码是:
public struct RationalNumber
{
private readonly long numerator;
private readonly long denominator;
private bool isDefinitelyCoprime;
private static RationalNumber zero = 0;
public RationalNumber(long numerator, long denominator)
{
this.numerator = numerator;
this.denominator = denominator;
this.isDefinitelyCoprime = false;
}
...
}
Run Code Online (Sandbox Code Playgroud)
目前我正在实现一个RationalMatrix,你可能已经猜到,它将由RationalNumber类型元素组成.
我正在创建静态构建器的有用矩阵是Identity矩阵.代码如下:
public static RationalMatrix GetIdentityMatrix(int dimension)
{
RationalNumber[,] values = new RationalNumber[dimension, dimension];
for (int i = 0; i < dimension; i++)
values[i, i] = 1;
return new RationalMatrix(values);
} …Run Code Online (Sandbox Code Playgroud) 乍一看荒谬的模式匹配,请考虑以下内容:
string s = null;
if (s is string ss) //false
if (s is string) //false
Run Code Online (Sandbox Code Playgroud)
两者都is将回归false.但是,如果我们var完全使用行为更改:
string s = null;
if (s is var ss) //true!?!
Run Code Online (Sandbox Code Playgroud)
如果你将鼠标悬停var在VS2017中,那么类型是完全不同string的行为is.即使推断类型相同,编译器也会做一些截然不同的事情.怎么会这样?这是一个错误吗?这种null类型不知何故冒出来了吗?