我的Delphi 7代码中有以下声明。
TMyCharSet = set of char;
当我将该代码迁移到Delphi XE4时,我在上面的行中收到了编译器警告。
W1050 WideChar reduced to byte char in set expressions. Consider using 'CharInSet' function in 'SysUtils' unit.
我应该如何重新声明TMyCharSet?
答案 0 :(得分:7)
一个集合不能包含大于一个字节的项目。由于UniCode Delphi中的Char
是WideChar
,其大小为两个字节,因此集合类型是不合适的容器。
以下是基于记录TSet<T>
的通用集类型的示例。这意味着您不必考虑创建和销毁此类变量。将此类型用作简单类型的容器。我试图模仿集合类型的大部分行为。
项目的加法和减法可以使用+和 - 运算符完成。还添加了in
运算符。
注意:记录将数据保存在动态数组中。将变量分配给另一个变量将使两个变量使用相同的动态数组。内置的写时复制(COW)保护将防止一个变量的更改反映在另一个变量上。
unit GenericSet;
interface
Uses
System.Generics.Defaults;
Type
TSet<T> = record
class operator Add(const aSet: TSet<T>; aValue: T) : TSet<T>; overload;
class operator Add(const aSet: TSet<T>; const aSetOfT: TArray<T>) : TSet<T>; overload;
class operator Add(const aSet1: TSet<T>; const aSet2: TSet<T>) : TSet<T>; overload;
class operator Subtract(const aSet: TSet<T>; aValue: T): TSet<T>; overload;
class operator Subtract(const aSet: TSet<T>; const aSetOfT: TArray<T>) : TSet<T>; overload;
class operator Subtract(const aSet1: TSet<T>; const aSet2: TSet<T>) : TSet<T>; overload;
class operator In(aValue: T; const aSet: TSet<T>): Boolean; overload;
class operator In(const aSetOf: TArray<T>; const aSet: TSet<T>): Boolean; overload;
class operator In(const aSet1: TSet<T>; const aSet2: TSet<T>): Boolean; overload;
private
FSetArray : TArray<T>;
function GetEmpty: Boolean;
public
procedure Add(aValue: T);
procedure AddSet(const setOfT: array of T); overload;
procedure AddSet(const aSet: TSet<T>); overload;
procedure Remove(aValue: T);
procedure RemoveSet(const setOfT: array of T); overload;
procedure RemoveSet(const aSet : TSet<T>); overload;
function Contains(aValue: T): Boolean; overload;
function Contains(const aSetOfT: array of T): Boolean; overload;
function Contains(const aSet : TSet<T>): Boolean; overload;
procedure Clear;
property Empty: Boolean read GetEmpty;
end;
implementation
procedure TSet<T>.Add(aValue: T);
begin
if not Contains(aValue) then begin
SetLength(FSetArray,Length(FSetArray)+1);
FSetArray[Length(FSetArray)-1] := aValue;
end;
end;
class operator TSet<T>.Add(const aSet: TSet<T>; aValue: T): TSet<T>;
begin
Result.AddSet(aSet.FSetArray);
Result.Add(aValue);
end;
class operator TSet<T>.Add(const aSet: TSet<T>; const aSetOfT: TArray<T>): TSet<T>;
begin
Result.AddSet(aSet.FSetArray);
Result.AddSet(aSetOfT);
end;
class operator TSet<T>.Add(const aSet1, aSet2: TSet<T>): TSet<T>;
begin
Result.AddSet(aSet1.FSetArray);
Result.AddSet(aSet2.FSetArray);
end;
procedure TSet<T>.AddSet(const setOfT: array of T);
var
i : Integer;
begin
for i := 0 to High(setOfT) do
Self.Add(setOfT[i]);
end;
procedure TSet<T>.AddSet(const aSet: TSet<T>);
begin
AddSet(aSet.FSetArray);
end;
procedure TSet<T>.RemoveSet(const setOfT: array of T);
var
i : Integer;
begin
for i := 0 to High(setOfT) do
Self.Remove(setOfT[i]);
end;
procedure TSet<T>.RemoveSet(const aSet: TSet<T>);
begin
RemoveSet(aSet.FSetArray);
end;
class operator TSet<T>.Subtract(const aSet1, aSet2: TSet<T>): TSet<T>;
begin
Result.AddSet(aSet1.FSetArray);
Result.RemoveSet(aSet2.FSetArray);
end;
class operator TSet<T>.Subtract(const aSet: TSet<T>;
const aSetOfT: TArray<T>): TSet<T>;
begin
Result.AddSet(aSet.FSetArray);
Result.RemoveSet(aSetOfT);
end;
class operator TSet<T>.Subtract(const aSet: TSet<T>; aValue: T): TSet<T>;
begin
Result.AddSet(aSet.FSetArray);
Result.RemoveSet(aValue);
end;
class operator TSet<T>.In(aValue: T; const aSet: TSet<T>): Boolean;
begin
Result := aSet.Contains(aValue);
end;
class operator TSet<T>.In(const aSetOf: TArray<T>; const aSet: TSet<T>): Boolean;
begin
Result := aSet.Contains(aSetOf);
end;
class operator TSet<T>.In(const aSet1: TSet<T>; const aSet2: TSet<T>): Boolean;
begin
Result := aSet2.Contains(aSet1.FSetArray);
end;
function TSet<T>.Contains(aValue: T): Boolean;
var
i : Integer;
c : IEqualityComparer<T>;
begin
c := TEqualityComparer<T>.Default;
Result := false;
for i := 0 to Length(FSetArray)-1 do
if c.Equals(FSetArray[i],aValue) then
Exit(True);
end;
function TSet<T>.GetEmpty: Boolean;
begin
Result := (Length(FSetArray) = 0);
end;
procedure TSet<T>.Clear;
begin
SetLength(FSetArray,0);
end;
function TSet<T>.Contains(const aSetOfT: array of T): Boolean;
var
i : Integer;
begin
Result := High(aSetOfT) >= 0;
for i := 0 to High(aSetOfT) do
begin
Result := Contains(ASetOfT[i]);
if not Result then
Exit(false);
end;
end;
function TSet<T>.Contains(const aSet: TSet<T>): Boolean;
begin
Result := Contains(aSet.FSetArray);
end;
procedure TSet<T>.Remove(aValue: T);
var
i : Integer;
c : IEqualityComparer<T>;
begin
c := TEqualityComparer<T>.Default;
for i := 0 to Length(FSetArray)-1 do
begin
if c.Equals(FSetArray[i],aValue) then
begin
SetLength(FSetArray,Length(FSetArray)); // Ensure unique dyn array
if (i < Length(FSetArray)-1) then
FSetArray[i] := FSetArray[Length(FSetArray)-1]; // Move last element
SetLength(FSetArray,Length(FSetArray)-1);
Break;
end;
end;
end;
end.
示例测试程序:
program ProjectGenericSet;
{$APPTYPE CONSOLE}
uses
GenericSet in 'GenericSet.pas';
var
mySet,mySet1 : TSet<Char>;
begin
mySet.AddSet(['A','B','C']);
WriteLn(mySet.Contains('C'));
WriteLn(mySet.Contains('D')); // False
mySet := mySet + 'D';
WriteLn(mySet.Contains('D'));
WriteLn('D' in mySet);
mySet := mySet - 'D';
WriteLn(mySet.Contains('D')); // False
mySet := mySet + TArray<Char>.Create('D','E');
WriteLn(mySet.Contains('D'));
WriteLn(mySet.Contains(['A','D']));
mySet1 := mySet;
// Testing COW
mySet1.Remove('A');
WriteLn(mySet.Contains('A'));
mySet1:= mySet1 + mySet;
WriteLn(mySet1.Contains('A'));
mySet := mySet1;
mySet1.Clear;
WriteLn(mySet.Contains('A'));
ReadLn;
end.
答案 1 :(得分:3)
您收到警告,因为XE4使用WideChar作为Char类型的变量(和WideString for String),因此Char现在占用2个字节而不是1个字节。现在可以在String / Char中保留unicode字符,但出于同样的原因,不再使用char集合(在Delphi中它是固定大小,32字节位映射,因此最多可以保留256个项目)。 / p>
如果你只使用#0 ..#127范围内的字符(只有拉丁/常规符号),那么你可以只替换Char - &gt; AnsiChar(但是当你从Char中分配它时你会看到另一个警告,你将不得不使用显式类型转换来抑制它)。
如果你需要国家/ unicode符号,那么Delphi中没有“随时可用”的结构,但你可以使用Tdictionary来达到这个目的:
type
TEmptyRecord = record end;
TSet<T> = class(TDictionary<T,TEmptyRecord>)
public
procedure Add(Value: T); reintroduce; inline;
procedure AddOrSetValue(Value: T); reintroduce; inline;
function Contains(Value: T):Boolean; reintroduce; inline;
end;
procedure TSet<T>.Add(Value: T);
var Dummy: TEmptyRecord;
begin
inherited AddOrSetValue(Value, Dummy);
end;
procedure TSet<T>.AddOrSetValue(Value: T);
var Dummy: TEmptyRecord;
begin
inherited AddOrSetValue(Value, Dummy);
end;
function TSet<T>.Contains(Value: T): Boolean;
begin
result := inherited ContainsKey(Value);
end;
当然,您将按照任何其他常规课程进行初始化。 但它仍然非常有效(当然不是那么快“集合”,只是因为“set”总是受到最大尺寸256项但高度优化的限制)。
或者你可以为unicode字符创建你自己的set类作为位映射,它需要8kb的内存来保存所有位,并且几乎和“set of”一样快。
答案 2 :(得分:0)
请参阅网络上的四项建议:
if not (CharInSet(Key,['0'..'9',#8]) then key := #0;
来自:http://www.activedelphi.com.br/forum/viewtopic.php?t=66035&sid=f5838cc7dc991f7b3340e4e2689b222a